refactor: simplify shell generator

This commit is contained in:
ReaJason
2025-05-28 01:22:54 +08:00
parent 9692ab6e7b
commit 566286a8fa
10 changed files with 120 additions and 209 deletions
@@ -0,0 +1,18 @@
package com.reajason.javaweb.memshell.generator;
import com.reajason.javaweb.memshell.config.ShellConfig;
import com.reajason.javaweb.memshell.config.ShellToolConfig;
/**
* @author ReaJason
* @since 2025/5/27
*/
public abstract class ASMShellGenerator<T extends ShellToolConfig> implements ShellGenerator {
protected final ShellConfig shellConfig;
protected final T shellToolConfig;
protected ASMShellGenerator(ShellConfig shellConfig, T shellToolConfig) {
this.shellConfig = shellConfig;
this.shellToolConfig = shellToolConfig;
}
}
@@ -1,14 +1,8 @@
package com.reajason.javaweb.memshell.generator; package com.reajason.javaweb.memshell.generator;
import com.reajason.javaweb.ClassBytesShrink;
import com.reajason.javaweb.buddy.LogRemoveMethodVisitor;
import com.reajason.javaweb.buddy.ServletRenameVisitorWrapper;
import com.reajason.javaweb.buddy.TargetJreVersionVisitorWrapper;
import com.reajason.javaweb.memshell.config.AntSwordConfig; import com.reajason.javaweb.memshell.config.AntSwordConfig;
import com.reajason.javaweb.memshell.config.ShellConfig; import com.reajason.javaweb.memshell.config.ShellConfig;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.DynamicType; import net.bytebuddy.dynamic.DynamicType;
import org.apache.commons.lang3.StringUtils;
import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.named;
@@ -16,44 +10,15 @@ import static net.bytebuddy.matcher.ElementMatchers.named;
* @author ReaJason * @author ReaJason
* @since 2025/02/18 * @since 2025/02/18
*/ */
public class AntSwordGenerator { public class AntSwordGenerator extends ByteBuddyShellGenerator<AntSwordConfig> {
private final ShellConfig shellConfig; public AntSwordGenerator(ShellConfig shellConfig, AntSwordConfig shellToolConfig) {
private final AntSwordConfig antSwordConfig; super(shellConfig, shellToolConfig);
public AntSwordGenerator(ShellConfig shellConfig, AntSwordConfig antSwordConfig) {
this.shellConfig = shellConfig;
this.antSwordConfig = antSwordConfig;
} }
public DynamicType.Builder<?> getBuilder() { @Override
if (antSwordConfig.getShellClass() == null) { protected DynamicType.Builder<?> build(DynamicType.Builder<?> builder) {
throw new IllegalArgumentException("antSwordConfig.getClazz() == null"); return builder.field(named("pass")).value(shellToolConfig.getPass())
} .field(named("headerName")).value(shellToolConfig.getHeaderName())
if (StringUtils.isBlank(antSwordConfig.getPass())) { .field(named("headerValue")).value(shellToolConfig.getHeaderValue());
throw new IllegalArgumentException("antSwordConfig.getPass().isBlank()");
}
DynamicType.Builder<?> builder = new ByteBuddy()
.redefine(antSwordConfig.getShellClass())
.name(antSwordConfig.getShellClassName())
.visit(new TargetJreVersionVisitorWrapper(shellConfig.getTargetJreVersion()));
if (shellConfig.isJakarta()) {
builder = builder.visit(ServletRenameVisitorWrapper.INSTANCE);
}
if (shellConfig.isDebugOff()) {
builder = LogRemoveMethodVisitor.extend(builder);
}
return builder.field(named("pass")).value(antSwordConfig.getPass())
.field(named("headerName")).value(antSwordConfig.getHeaderName())
.field(named("headerValue")).value(antSwordConfig.getHeaderValue());
}
public byte[] getBytes() {
DynamicType.Builder<?> builder = getBuilder();
try (DynamicType.Unloaded<?> make = builder.make()) {
return ClassBytesShrink.shrink(make.getBytes(), shellConfig.isShrink());
}
} }
} }
@@ -1,15 +1,9 @@
package com.reajason.javaweb.memshell.generator; package com.reajason.javaweb.memshell.generator;
import com.reajason.javaweb.ClassBytesShrink;
import com.reajason.javaweb.buddy.LogRemoveMethodVisitor;
import com.reajason.javaweb.buddy.ServletRenameVisitorWrapper;
import com.reajason.javaweb.buddy.TargetJreVersionVisitorWrapper;
import com.reajason.javaweb.memshell.config.BehinderConfig; import com.reajason.javaweb.memshell.config.BehinderConfig;
import com.reajason.javaweb.memshell.config.ShellConfig; import com.reajason.javaweb.memshell.config.ShellConfig;
import com.reajason.javaweb.memshell.utils.DigestUtils; import com.reajason.javaweb.memshell.utils.DigestUtils;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.DynamicType; import net.bytebuddy.dynamic.DynamicType;
import org.apache.commons.lang3.StringUtils;
import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.named;
@@ -17,46 +11,15 @@ import static net.bytebuddy.matcher.ElementMatchers.named;
* @author ReaJason * @author ReaJason
* @since 2024/12/21 * @since 2024/12/21
*/ */
public class BehinderGenerator { public class BehinderGenerator extends ByteBuddyShellGenerator<BehinderConfig> {
private final ShellConfig shellConfig; public BehinderGenerator(ShellConfig shellConfig, BehinderConfig shellToolConfig) {
private final BehinderConfig behinderConfig; super(shellConfig, shellToolConfig);
public BehinderGenerator(ShellConfig shellConfig, BehinderConfig behinderConfig) {
this.shellConfig = shellConfig;
this.behinderConfig = behinderConfig;
}
public DynamicType.Builder<?> getBuilder() {
if (behinderConfig.getShellClass() == null) {
throw new IllegalArgumentException("behinderConfig.getClazz() == null");
}
if (StringUtils.isBlank(behinderConfig.getPass())) {
throw new IllegalArgumentException("behinderConfig.getPass().isBlank()");
}
String md5Key = DigestUtils.md5Hex(behinderConfig.getPass()).substring(0, 16);
DynamicType.Builder<?> builder = new ByteBuddy()
.redefine(behinderConfig.getShellClass())
.name(behinderConfig.getShellClassName())
.visit(new TargetJreVersionVisitorWrapper(shellConfig.getTargetJreVersion()));
if (shellConfig.isJakarta()) {
builder = builder.visit(ServletRenameVisitorWrapper.INSTANCE);
}
if (shellConfig.isDebugOff()) {
builder = LogRemoveMethodVisitor.extend(builder);
} }
public DynamicType.Builder<?> build(DynamicType.Builder<?> builder) {
String md5Key = DigestUtils.md5Hex(shellToolConfig.getPass()).substring(0, 16);
return builder.field(named("pass")).value(md5Key) return builder.field(named("pass")).value(md5Key)
.field(named("headerName")).value(behinderConfig.getHeaderName()) .field(named("headerName")).value(shellToolConfig.getHeaderName())
.field(named("headerValue")).value(behinderConfig.getHeaderValue()); .field(named("headerValue")).value(shellToolConfig.getHeaderValue());
}
public byte[] getBytes() {
DynamicType.Builder<?> builder = getBuilder();
try (DynamicType.Unloaded<?> make = builder.make()) {
return ClassBytesShrink.shrink(make.getBytes(), shellConfig.isShrink());
}
} }
} }
@@ -0,0 +1,47 @@
package com.reajason.javaweb.memshell.generator;
import com.reajason.javaweb.ClassBytesShrink;
import com.reajason.javaweb.buddy.ByPassJavaModuleInterceptor;
import com.reajason.javaweb.buddy.LogRemoveMethodVisitor;
import com.reajason.javaweb.buddy.ServletRenameVisitorWrapper;
import com.reajason.javaweb.buddy.TargetJreVersionVisitorWrapper;
import com.reajason.javaweb.memshell.config.ShellConfig;
import com.reajason.javaweb.memshell.config.ShellToolConfig;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.DynamicType;
/**
* @author ReaJason
* @since 2025/5/27
*/
public abstract class ByteBuddyShellGenerator<T extends ShellToolConfig> implements ShellGenerator {
protected final ShellConfig shellConfig;
protected final T shellToolConfig;
protected ByteBuddyShellGenerator(ShellConfig shellConfig, T shellToolConfig) {
this.shellConfig = shellConfig;
this.shellToolConfig = shellToolConfig;
}
protected abstract DynamicType.Builder<?> build(DynamicType.Builder<?> builder);
@Override
public byte[] getBytes() {
DynamicType.Builder<?> builder = build(new ByteBuddy()
.redefine(shellToolConfig.getShellClass())
.name(shellToolConfig.getShellClassName())
.visit(new TargetJreVersionVisitorWrapper(shellConfig.getTargetJreVersion())));
if (shellConfig.isJakarta()) {
builder = builder.visit(ServletRenameVisitorWrapper.INSTANCE);
}
if (shellConfig.isDebugOff()) {
builder = LogRemoveMethodVisitor.extend(builder);
}
try (DynamicType.Unloaded<?> unloaded = builder.make()) {
return ClassBytesShrink.shrink(unloaded.getBytes(), shellConfig.isShrink());
}
}
}
@@ -12,25 +12,21 @@ import java.util.Base64;
* @author ReaJason * @author ReaJason
* @since 2025/3/18 * @since 2025/3/18
*/ */
public class CustomShellGenerator { public class CustomShellGenerator extends ASMShellGenerator<CustomConfig> {
private final ShellConfig shellConfig;
private final CustomConfig customConfig;
public CustomShellGenerator(ShellConfig shellConfig, CustomConfig customConfig) { public CustomShellGenerator(ShellConfig shellConfig, CustomConfig customConfig) {
this.shellConfig = shellConfig; super(shellConfig, customConfig);
this.customConfig = customConfig;
} }
@Override
public byte[] getBytes() { public byte[] getBytes() {
String shellClassBase64 = customConfig.getShellClassBase64(); String shellClassBase64 = shellToolConfig.getShellClassBase64();
if (StringUtils.isBlank(shellClassBase64)) { if (StringUtils.isBlank(shellClassBase64)) {
throw new IllegalArgumentException("Custom shell class is empty"); throw new IllegalArgumentException("Custom shell class is empty");
} }
byte[] classBytes = Base64.getDecoder().decode(shellClassBase64);
byte[] bytes = ClassRenameUtils.renameClass(Base64.getDecoder().decode(shellClassBase64), customConfig.getShellClassName()); byte[] bytes = ClassRenameUtils.renameClass(classBytes, shellToolConfig.getShellClassName());
return ClassBytesShrink.shrink(bytes, shellConfig.isShrink()); return ClassBytesShrink.shrink(bytes, shellConfig.isShrink());
} }
} }
@@ -1,15 +1,10 @@
package com.reajason.javaweb.memshell.generator; package com.reajason.javaweb.memshell.generator;
import com.reajason.javaweb.ClassBytesShrink; import com.reajason.javaweb.ClassBytesShrink;
import com.reajason.javaweb.buddy.LogRemoveMethodVisitor;
import com.reajason.javaweb.buddy.ServletRenameVisitorWrapper;
import com.reajason.javaweb.buddy.TargetJreVersionVisitorWrapper;
import com.reajason.javaweb.memshell.config.GodzillaConfig; import com.reajason.javaweb.memshell.config.GodzillaConfig;
import com.reajason.javaweb.memshell.config.ShellConfig; import com.reajason.javaweb.memshell.config.ShellConfig;
import com.reajason.javaweb.memshell.utils.DigestUtils; import com.reajason.javaweb.memshell.utils.DigestUtils;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.DynamicType; import net.bytebuddy.dynamic.DynamicType;
import org.apache.commons.lang3.StringUtils;
import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.named;
@@ -17,49 +12,20 @@ import static net.bytebuddy.matcher.ElementMatchers.named;
* @author ReaJason * @author ReaJason
* @since 2024/11/23 * @since 2024/11/23
*/ */
public class GodzillaGenerator { public class GodzillaGenerator extends ByteBuddyShellGenerator<GodzillaConfig> {
private final ShellConfig shellConfig;
private final GodzillaConfig godzillaConfig;
public GodzillaGenerator(ShellConfig shellConfig, GodzillaConfig godzillaConfig) { public GodzillaGenerator(ShellConfig shellConfig, GodzillaConfig godzillaConfig) {
this.shellConfig = shellConfig; super(shellConfig, godzillaConfig);
this.godzillaConfig = godzillaConfig;
} }
public DynamicType.Builder<?> getBuilder() { @Override
if (godzillaConfig.getShellClass() == null) { public DynamicType.Builder<?> build(DynamicType.Builder<?> builder) {
throw new IllegalArgumentException("godzillaConfig.getClazz() == null"); String md5Key = DigestUtils.md5Hex(shellToolConfig.getKey()).substring(0, 16);
} String md5 = DigestUtils.md5Hex(shellToolConfig.getPass() + md5Key).toUpperCase();
if (StringUtils.isBlank(godzillaConfig.getKey()) || StringUtils.isBlank(godzillaConfig.getPass())) { return builder.field(named("pass")).value(shellToolConfig.getPass())
throw new IllegalArgumentException("godzillaConfig.getKey().isBlank() || godzillaConfig.getPass().isBlank()");
}
String md5Key = DigestUtils.md5Hex(godzillaConfig.getKey()).substring(0, 16);
String md5 = DigestUtils.md5Hex(godzillaConfig.getPass() + md5Key).toUpperCase();
DynamicType.Builder<?> builder = new ByteBuddy()
.redefine(godzillaConfig.getShellClass())
.name(godzillaConfig.getShellClassName())
.visit(new TargetJreVersionVisitorWrapper(shellConfig.getTargetJreVersion()));
if (shellConfig.isJakarta()) {
builder = builder.visit(ServletRenameVisitorWrapper.INSTANCE);
}
if (shellConfig.isDebugOff()) {
builder = LogRemoveMethodVisitor.extend(builder);
}
return builder.field(named("pass")).value(godzillaConfig.getPass())
.field(named("key")).value(md5Key) .field(named("key")).value(md5Key)
.field(named("md5")).value(md5) .field(named("md5")).value(md5)
.field(named("headerName")).value(godzillaConfig.getHeaderName()) .field(named("headerName")).value(shellToolConfig.getHeaderName())
.field(named("headerValue")).value(godzillaConfig.getHeaderValue()); .field(named("headerValue")).value(shellToolConfig.getHeaderValue());
}
public byte[] getBytes() {
DynamicType.Builder<?> builder = getBuilder();
try (DynamicType.Unloaded<?> make = builder.make()) {
return ClassBytesShrink.shrink(make.getBytes(), shellConfig.isShrink());
}
} }
} }
@@ -1,12 +1,7 @@
package com.reajason.javaweb.memshell.generator; package com.reajason.javaweb.memshell.generator;
import com.reajason.javaweb.ClassBytesShrink;
import com.reajason.javaweb.buddy.LogRemoveMethodVisitor;
import com.reajason.javaweb.buddy.ServletRenameVisitorWrapper;
import com.reajason.javaweb.buddy.TargetJreVersionVisitorWrapper;
import com.reajason.javaweb.memshell.config.NeoreGeorgConfig; import com.reajason.javaweb.memshell.config.NeoreGeorgConfig;
import com.reajason.javaweb.memshell.config.ShellConfig; import com.reajason.javaweb.memshell.config.ShellConfig;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.DynamicType; import net.bytebuddy.dynamic.DynamicType;
import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.named;
@@ -15,37 +10,14 @@ import static net.bytebuddy.matcher.ElementMatchers.named;
* @author ReaJason * @author ReaJason
* @since 2025/2/12 * @since 2025/2/12
*/ */
public class NeoreGeorgGenerator { public class NeoreGeorgGenerator extends ByteBuddyShellGenerator<NeoreGeorgConfig> {
private final ShellConfig shellConfig;
private final NeoreGeorgConfig neoreGeorgConfig;
public NeoreGeorgGenerator(ShellConfig shellConfig, NeoreGeorgConfig neoreGeorgConfig) { public NeoreGeorgGenerator(ShellConfig shellConfig, NeoreGeorgConfig neoreGeorgConfig) {
this.shellConfig = shellConfig; super(shellConfig, neoreGeorgConfig);
this.neoreGeorgConfig = neoreGeorgConfig;
} }
public DynamicType.Builder<?> getBuilder() { @Override
DynamicType.Builder<?> builder = new ByteBuddy() protected DynamicType.Builder<?> build(DynamicType.Builder<?> builder) {
.redefine(neoreGeorgConfig.getShellClass()) return builder.field(named("headerName")).value(shellToolConfig.getHeaderName())
.name(neoreGeorgConfig.getShellClassName()) .field(named("headerValue")).value(shellToolConfig.getHeaderValue());
.visit(new TargetJreVersionVisitorWrapper(shellConfig.getTargetJreVersion()));
if (shellConfig.isJakarta()) {
builder = builder.visit(ServletRenameVisitorWrapper.INSTANCE);
}
if (shellConfig.isDebugOff()) {
builder = LogRemoveMethodVisitor.extend(builder);
}
return builder.field(named("headerName")).value(neoreGeorgConfig.getHeaderName())
.field(named("headerValue")).value(neoreGeorgConfig.getHeaderValue());
}
public byte[] getBytes() {
DynamicType.Builder<?> builder = getBuilder();
try (DynamicType.Unloaded<?> make = builder.make()) {
return ClassBytesShrink.shrink(make.getBytes(), shellConfig.isShrink());
}
} }
} }
@@ -0,0 +1,10 @@
package com.reajason.javaweb.memshell.generator;
/**
* @author ReaJason
* @since 2025/5/27
*/
public interface ShellGenerator {
byte[] getBytes();
}
@@ -1,12 +1,7 @@
package com.reajason.javaweb.memshell.generator; package com.reajason.javaweb.memshell.generator;
import com.reajason.javaweb.ClassBytesShrink;
import com.reajason.javaweb.buddy.LogRemoveMethodVisitor;
import com.reajason.javaweb.buddy.ServletRenameVisitorWrapper;
import com.reajason.javaweb.buddy.TargetJreVersionVisitorWrapper;
import com.reajason.javaweb.memshell.config.ShellConfig; import com.reajason.javaweb.memshell.config.ShellConfig;
import com.reajason.javaweb.memshell.config.Suo5Config; import com.reajason.javaweb.memshell.config.Suo5Config;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.DynamicType; import net.bytebuddy.dynamic.DynamicType;
import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.named;
@@ -15,38 +10,16 @@ import static net.bytebuddy.matcher.ElementMatchers.named;
* @author ReaJason * @author ReaJason
* @since 2025/2/12 * @since 2025/2/12
*/ */
public class Suo5Generator { public class Suo5Generator extends ByteBuddyShellGenerator<Suo5Config> {
private final ShellConfig shellConfig;
private final Suo5Config suo5Config;
public Suo5Generator(ShellConfig shellConfig, Suo5Config suo5Config) { public Suo5Generator(ShellConfig shellConfig, Suo5Config suo5Config) {
this.shellConfig = shellConfig; super(shellConfig, suo5Config);
this.suo5Config = suo5Config;
}
public DynamicType.Builder<?> getBuilder() {
DynamicType.Builder<?> builder = new ByteBuddy()
.redefine(suo5Config.getShellClass())
.name(suo5Config.getShellClassName())
.visit(new TargetJreVersionVisitorWrapper(shellConfig.getTargetJreVersion()));
if (shellConfig.isJakarta()) {
builder = builder.visit(ServletRenameVisitorWrapper.INSTANCE);
}
if (shellConfig.isDebugOff()) {
builder = LogRemoveMethodVisitor.extend(builder);
} }
@Override
protected DynamicType.Builder<?> build(DynamicType.Builder<?> builder) {
return builder return builder
.field(named("headerName")).value(suo5Config.getHeaderName()) .field(named("headerName")).value(shellToolConfig.getHeaderName())
.field(named("headerValue")).value(suo5Config.getHeaderValue()); .field(named("headerValue")).value(shellToolConfig.getHeaderValue());
}
public byte[] getBytes() {
DynamicType.Builder<?> builder = getBuilder();
try (DynamicType.Unloaded<?> make = builder.make()) {
return ClassBytesShrink.shrink(make.getBytes(), shellConfig.isShrink());
}
} }
} }
@@ -7,6 +7,7 @@ import com.reajason.javaweb.memshell.config.GodzillaConfig;
import com.reajason.javaweb.memshell.config.ShellConfig; import com.reajason.javaweb.memshell.config.ShellConfig;
import com.reajason.javaweb.memshell.shelltool.godzilla.GodzillaServlet; import com.reajason.javaweb.memshell.shelltool.godzilla.GodzillaServlet;
import com.reajason.javaweb.memshell.utils.CommonUtil; import com.reajason.javaweb.memshell.utils.CommonUtil;
import com.reajason.javaweb.util.ClassDefiner;
import com.reajason.javaweb.util.ClassUtils; import com.reajason.javaweb.util.ClassUtils;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import net.bytebuddy.dynamic.DynamicType; import net.bytebuddy.dynamic.DynamicType;
@@ -38,8 +39,8 @@ class GodzillaGeneratorTest {
.key("key") .key("key")
.headerName("User-Agent") .headerName("User-Agent")
.headerValue("test").build(); .headerValue("test").build();
DynamicType.Builder<?> builder = new GodzillaGenerator(shellConfig, godzillaConfig).getBuilder(); byte[] bytes = new GodzillaGenerator(shellConfig, godzillaConfig).getBytes();
Class<?> loaded = builder.make().load(this.getClass().getClassLoader()).getLoaded(); Class<?> loaded = ClassDefiner.defineClass(bytes);
Object o = loaded.getDeclaredConstructor().newInstance(); Object o = loaded.getDeclaredConstructor().newInstance();
assertEquals(godzillaConfig.getPass(), ClassUtils.getFieldValue(o, "pass")); assertEquals(godzillaConfig.getPass(), ClassUtils.getFieldValue(o, "pass"));
assertEquals(godzillaConfig.getHeaderName(), ClassUtils.getFieldValue(o, "headerName")); assertEquals(godzillaConfig.getHeaderName(), ClassUtils.getFieldValue(o, "headerName"));