mirror of
https://github.com/ReaJason/MemShellParty.git
synced 2026-09-21 22:50:42 +08:00
feat: support packer config
This commit is contained in:
@@ -5,7 +5,7 @@ plugins {
|
||||
}
|
||||
|
||||
group = "io.github.reajason"
|
||||
description = "Java deserialize payload for MemShellParty"
|
||||
description = "Java payload packer for MemShellParty"
|
||||
version = rootProject.version
|
||||
|
||||
dependencies {
|
||||
@@ -22,9 +22,6 @@ dependencies {
|
||||
}
|
||||
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion = JavaLanguageVersion.of(8)
|
||||
}
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
package com.reajason.javaweb.packer;
|
||||
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2025/1/26
|
||||
*/
|
||||
public interface AggregatePacker extends Packer {
|
||||
|
||||
/**
|
||||
* 聚合打包当前所有分类下的 payload
|
||||
*
|
||||
* @param config 生成结果
|
||||
* @return key - 打包名称,value - 打包 payload
|
||||
*/
|
||||
default Map<String, String> packAll(ClassPackerConfig config) {
|
||||
return Packers.getPackersWithParent(this.getClass()).stream().collect(Collectors.toMap(
|
||||
Enum::name,
|
||||
packers -> {
|
||||
try {
|
||||
return packers.getInstance().pack(config);
|
||||
} catch (Exception e) {
|
||||
return e.getMessage();
|
||||
}
|
||||
},
|
||||
(existing, replacement) -> existing,
|
||||
LinkedHashMap::new
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* 将第一个 sub packer 作为默认输出
|
||||
*
|
||||
* @param config 生成的内存马信息
|
||||
* @return payload
|
||||
*/
|
||||
@Override
|
||||
default String pack(ClassPackerConfig config) {
|
||||
List<Packers> packersWithParent = Packers.getPackersWithParent(this.getClass());
|
||||
if (packersWithParent.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return packersWithParent.get(0).getInstance().pack(config);
|
||||
}
|
||||
}
|
||||
@@ -7,9 +7,10 @@ import lombok.Data;
|
||||
* @since 2025/6/27
|
||||
*/
|
||||
@Data
|
||||
public class ClassPackerConfig {
|
||||
public class ClassPackerConfig<T> {
|
||||
private String className;
|
||||
private byte[] classBytes;
|
||||
private String classBytesBase64Str;
|
||||
private boolean byPassJavaModule;
|
||||
private T customConfig;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,8 @@ import java.util.Map;
|
||||
* @since 2025/6/27
|
||||
*/
|
||||
@Data
|
||||
public class JarPackerConfig {
|
||||
public class JarPackerConfig<T> {
|
||||
private String mainClassName;
|
||||
private transient Map<String, byte[]> classBytes;
|
||||
private T customConfig;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,19 @@
|
||||
package com.reajason.javaweb.packer;
|
||||
|
||||
import com.reajason.javaweb.packer.spec.PackerSchema;
|
||||
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2024/11/26
|
||||
*/
|
||||
public interface Packer {
|
||||
public interface Packer<T> {
|
||||
TypeCache TYPE_CACHE = new TypeCache();
|
||||
|
||||
/**
|
||||
* 将自定义类打包成特定 payload
|
||||
@@ -12,7 +21,71 @@ public interface Packer {
|
||||
* @param classPackerConfig 自定义类信息
|
||||
* @return 字符串 payload
|
||||
*/
|
||||
default String pack(ClassPackerConfig classPackerConfig) {
|
||||
default String pack(ClassPackerConfig<T> classPackerConfig) {
|
||||
throw new UnsupportedOperationException("当前 " + this.getClass().getSimpleName() + " 不支持 string 生成");
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
default Class<T> customConfigType() {
|
||||
Optional<Class<?>> resolved = TYPE_CACHE.cache.computeIfAbsent(this.getClass(), TypeResolver::resolveCustomConfigType);
|
||||
return (Class<T>) resolved.orElse(null);
|
||||
}
|
||||
|
||||
default T resolveCustomConfig(Object rawCustomConfig) {
|
||||
Class<T> clazz = customConfigType();
|
||||
if (clazz == null) {
|
||||
return null;
|
||||
}
|
||||
return PackerConfigConverter.convert(rawCustomConfig, clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* Packer config schema for UI rendering.
|
||||
*/
|
||||
default PackerSchema schema() {
|
||||
return PackerSchema.empty();
|
||||
}
|
||||
|
||||
final class TypeCache {
|
||||
private final Map<Class<?>, Optional<Class<?>>> cache = new ConcurrentHashMap<>();
|
||||
}
|
||||
|
||||
final class TypeResolver {
|
||||
private TypeResolver() {
|
||||
}
|
||||
|
||||
private static Optional<Class<?>> resolveCustomConfigType(Class<?> clazz) {
|
||||
Class<?> current = clazz;
|
||||
while (current != null && current != Object.class) {
|
||||
Optional<Class<?>> fromInterfaces = resolveFromTypes(current.getGenericInterfaces());
|
||||
if (fromInterfaces.isPresent()) {
|
||||
return fromInterfaces;
|
||||
}
|
||||
current = current.getSuperclass();
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
private static Optional<Class<?>> resolveFromTypes(Type[] types) {
|
||||
for (Type type : types) {
|
||||
if (!(type instanceof ParameterizedType)) {
|
||||
continue;
|
||||
}
|
||||
ParameterizedType parameterizedType = (ParameterizedType) type;
|
||||
if (!(parameterizedType.getRawType() instanceof Class)) {
|
||||
continue;
|
||||
}
|
||||
Class<?> rawType = (Class<?>) parameterizedType.getRawType();
|
||||
if (rawType != Packer.class) {
|
||||
continue;
|
||||
}
|
||||
Type configType = parameterizedType.getActualTypeArguments()[0];
|
||||
if (configType instanceof Class) {
|
||||
return Optional.of((Class<?>) configType);
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.reajason.javaweb.packer;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
/**
|
||||
* Shared conversion helper for packer custom config.
|
||||
*/
|
||||
final class PackerConfigConverter {
|
||||
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
|
||||
|
||||
private PackerConfigConverter() {
|
||||
}
|
||||
|
||||
static <R> R convert(Object customConfig, Class<R> clazz) {
|
||||
if (customConfig == null) {
|
||||
return null;
|
||||
}
|
||||
if (clazz.isInstance(customConfig)) {
|
||||
return clazz.cast(customConfig);
|
||||
}
|
||||
try {
|
||||
return OBJECT_MAPPER.convertValue(customConfig, clazz);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new IllegalArgumentException("invalid customConfig for " + clazz.getSimpleName() + ": " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,8 @@
|
||||
package com.reajason.javaweb.packer;
|
||||
|
||||
import com.reajason.javaweb.packer.spec.PackerSchema;
|
||||
import com.reajason.javaweb.packer.aviator.AviatorPacker;
|
||||
import com.reajason.javaweb.packer.base64.Base64Packer;
|
||||
import com.reajason.javaweb.packer.base64.Base64URLEncoded;
|
||||
import com.reajason.javaweb.packer.base64.DefaultBase64Packer;
|
||||
import com.reajason.javaweb.packer.base64.GzipBase64Packer;
|
||||
import com.reajason.javaweb.packer.bsh.BeanShellPacker;
|
||||
import com.reajason.javaweb.packer.deserialize.hessian.Hessian2Packer;
|
||||
import com.reajason.javaweb.packer.deserialize.hessian.Hessian2XSLTScriptEnginePacker;
|
||||
@@ -52,7 +50,9 @@ import com.reajason.javaweb.packer.xmldecoder.XMLDecoderPacker;
|
||||
import com.reajason.javaweb.packer.xmldecoder.XMLDecoderScriptEnginePacker;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
@@ -67,20 +67,14 @@ public enum Packers {
|
||||
* Base64
|
||||
*/
|
||||
Base64(new Base64Packer()),
|
||||
DefaultBase64(new DefaultBase64Packer(), Base64Packer.class),
|
||||
Base64URLEncoded(new Base64URLEncoded(), Base64Packer.class),
|
||||
GzipBase64(new GzipBase64Packer(), Base64Packer.class),
|
||||
|
||||
/**
|
||||
* JSP 打包器
|
||||
*/
|
||||
JSP(new JspPacker()),
|
||||
ClassLoaderJSP(new ClassLoaderJspPacker(), JspPacker.class),
|
||||
ClassLoaderJSPUnicode(new ClassLoaderJspUnicodePacker(), JspPacker.class),
|
||||
DefineClassJSP(new DefineClassJspPacker(), JspPacker.class),
|
||||
DefineClassJSPUnicode(new DefineClassJspUnicodePacker(), JspPacker.class),
|
||||
JSPX(new JspxPacker(), JspPacker.class),
|
||||
JSPXUnicode(new JspxUnicodePacker(), JspPacker.class),
|
||||
|
||||
/**
|
||||
* BigInteger
|
||||
@@ -193,7 +187,48 @@ public enum Packers {
|
||||
this.parentPacker = parentPacker;
|
||||
}
|
||||
|
||||
public static List<Packers> getPackersWithParent(Class<?> parentPacker) {
|
||||
return Stream.of(Packers.values()).filter(p -> Objects.equals(p.getParentPacker(), parentPacker)).collect(Collectors.toList());
|
||||
public static Packers fromName(String name) {
|
||||
if (name == null) {
|
||||
throw new IllegalArgumentException("packer name is required");
|
||||
}
|
||||
try {
|
||||
return Packers.valueOf(name);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new IllegalArgumentException("unknown packer: " + name);
|
||||
}
|
||||
}
|
||||
|
||||
public String getCategoryName() {
|
||||
if (parentPacker == null) {
|
||||
return name();
|
||||
}
|
||||
for (Packers packer : Packers.values()) {
|
||||
if (packer.getParentPacker() == null && packer.getInstance().getClass().equals(parentPacker)) {
|
||||
return packer.name();
|
||||
}
|
||||
}
|
||||
return name();
|
||||
}
|
||||
|
||||
public boolean hasChildren() {
|
||||
return Stream.of(Packers.values()).anyMatch(p -> Objects.equals(p.getParentPacker(), this.getInstance().getClass()));
|
||||
}
|
||||
|
||||
public String getOutputKind() {
|
||||
return instance instanceof JarPacker ? "binary-base64" : "text";
|
||||
}
|
||||
|
||||
public PackerSchema getSchema() {
|
||||
PackerSchema schema = instance.schema();
|
||||
return schema == null ? PackerSchema.empty() : schema;
|
||||
}
|
||||
|
||||
public static Map<String, List<Packers>> groupedPackers() {
|
||||
Map<String, List<Packers>> groupedPackers = new LinkedHashMap<>();
|
||||
for (Packers packer : Packers.values()) {
|
||||
String groupName = packer.getCategoryName();
|
||||
groupedPackers.computeIfAbsent(groupName, k -> new java.util.ArrayList<>()).add(packer);
|
||||
}
|
||||
return groupedPackers;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,9 +3,12 @@ package com.reajason.javaweb.packer;
|
||||
import lombok.SneakyThrows;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Objects;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
@@ -19,4 +22,13 @@ public class Util {
|
||||
return IOUtils.toString(Objects.requireNonNull(stream), Charset.defaultCharset());
|
||||
}
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public static byte[] gzipCompress(byte[] data) {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
try (GZIPOutputStream gzip = new GZIPOutputStream(out)) {
|
||||
gzip.write(data);
|
||||
}
|
||||
return out.toByteArray();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.reajason.javaweb.packer.base64;
|
||||
|
||||
import com.reajason.javaweb.packer.spec.PackerFieldSchema;
|
||||
import com.reajason.javaweb.packer.spec.PackerFieldType;
|
||||
import com.reajason.javaweb.packer.spec.PackerSchema;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class Base64CustomPackerConfig {
|
||||
private boolean urlEncoded;
|
||||
private boolean gzipCompressed;
|
||||
|
||||
public static PackerSchema schema() {
|
||||
PackerFieldSchema urlEncodedField = new PackerFieldSchema();
|
||||
urlEncodedField.setKey("urlEncoded");
|
||||
urlEncodedField.setType(PackerFieldType.BOOLEAN);
|
||||
urlEncodedField.setRequired(false);
|
||||
urlEncodedField.setDefaultValue(false);
|
||||
urlEncodedField.setDescription("Enable URL encoding");
|
||||
urlEncodedField.setDescriptionI18nKey("urlEncoded.desc");
|
||||
|
||||
PackerFieldSchema gzipCompressedField = new PackerFieldSchema();
|
||||
gzipCompressedField.setKey("gzipCompressed");
|
||||
gzipCompressedField.setType(PackerFieldType.BOOLEAN);
|
||||
gzipCompressedField.setRequired(false);
|
||||
gzipCompressedField.setDefaultValue(false);
|
||||
gzipCompressedField.setDescription("Enable GZIP compression");
|
||||
gzipCompressedField.setDescriptionI18nKey("gzipCompressed.desc");
|
||||
|
||||
PackerSchema schema = new PackerSchema();
|
||||
schema.getFields().add(urlEncodedField);
|
||||
schema.getFields().add(gzipCompressedField);
|
||||
schema.getDefaultConfig().put("urlEncoded", false);
|
||||
schema.getDefaultConfig().put("gzipCompressed", false);
|
||||
return schema;
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,42 @@
|
||||
package com.reajason.javaweb.packer.base64;
|
||||
|
||||
import com.reajason.javaweb.packer.AggregatePacker;
|
||||
import com.reajason.javaweb.packer.ClassPackerConfig;
|
||||
import com.reajason.javaweb.packer.Packer;
|
||||
import com.reajason.javaweb.packer.Util;
|
||||
import com.reajason.javaweb.packer.spec.PackerSchema;
|
||||
import lombok.SneakyThrows;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2024/12/17
|
||||
*/
|
||||
public class Base64Packer implements AggregatePacker {
|
||||
}
|
||||
public class Base64Packer implements Packer<Base64CustomPackerConfig> {
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public String pack(ClassPackerConfig<Base64CustomPackerConfig> config) {
|
||||
Base64CustomPackerConfig customConfig = config.getCustomConfig();
|
||||
if (Objects.isNull(customConfig)) {
|
||||
return config.getClassBytesBase64Str();
|
||||
}
|
||||
byte[] bytes = config.getClassBytes();
|
||||
if (customConfig.isGzipCompressed()) {
|
||||
bytes = Util.gzipCompress(bytes);
|
||||
}
|
||||
String base64 = Base64.encodeBase64String(bytes);
|
||||
if (customConfig.isUrlEncoded()) {
|
||||
return URLEncoder.encode(base64, StandardCharsets.UTF_8.name());
|
||||
}
|
||||
return base64;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PackerSchema schema() {
|
||||
return Base64CustomPackerConfig.schema();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
package com.reajason.javaweb.packer.base64;
|
||||
|
||||
import com.reajason.javaweb.packer.ClassPackerConfig;
|
||||
import com.reajason.javaweb.packer.Packer;
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2025/9/2
|
||||
*/
|
||||
public class Base64URLEncoded implements Packer {
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public String pack(ClassPackerConfig config) {
|
||||
return URLEncoder.encode(config.getClassBytesBase64Str(), StandardCharsets.UTF_8.name());
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
package com.reajason.javaweb.packer.base64;
|
||||
|
||||
import com.reajason.javaweb.packer.ClassPackerConfig;
|
||||
import com.reajason.javaweb.packer.Packer;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2024/12/17
|
||||
*/
|
||||
public class DefaultBase64Packer implements Packer {
|
||||
@Override
|
||||
public String pack(ClassPackerConfig config) {
|
||||
return config.getClassBytesBase64Str();
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
package com.reajason.javaweb.packer.base64;
|
||||
|
||||
import com.reajason.javaweb.packer.ClassPackerConfig;
|
||||
import com.reajason.javaweb.packer.Packer;
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Base64;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2025/1/22
|
||||
*/
|
||||
public class GzipBase64Packer implements Packer {
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public String pack(ClassPackerConfig config) {
|
||||
return Base64.getEncoder().encodeToString(gzipCompress(config.getClassBytes()));
|
||||
}
|
||||
|
||||
public static byte[] gzipCompress(byte[] data) throws IOException {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
try (GZIPOutputStream gzip = new GZIPOutputStream(out)) {
|
||||
gzip.write(data);
|
||||
}
|
||||
return out.toByteArray();
|
||||
}
|
||||
}
|
||||
+8
-3
@@ -1,11 +1,16 @@
|
||||
package com.reajason.javaweb.packer.deserialize.hessian;
|
||||
|
||||
import com.reajason.javaweb.packer.AggregatePacker;
|
||||
import com.reajason.javaweb.packer.ClassPackerConfig;
|
||||
import com.reajason.javaweb.packer.Packer;
|
||||
import com.reajason.javaweb.packer.Packers;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2025/2/20
|
||||
*/
|
||||
public class Hessian2Packer implements AggregatePacker {
|
||||
|
||||
public class Hessian2Packer implements Packer {
|
||||
@Override
|
||||
public String pack(ClassPackerConfig classPackerConfig) {
|
||||
return Packers.Hessian2XSLTScriptEngine.getInstance().pack(classPackerConfig);
|
||||
}
|
||||
}
|
||||
|
||||
+8
-3
@@ -1,11 +1,16 @@
|
||||
package com.reajason.javaweb.packer.deserialize.hessian;
|
||||
|
||||
import com.reajason.javaweb.packer.AggregatePacker;
|
||||
import com.reajason.javaweb.packer.ClassPackerConfig;
|
||||
import com.reajason.javaweb.packer.Packer;
|
||||
import com.reajason.javaweb.packer.Packers;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2025/2/20
|
||||
*/
|
||||
public class HessianPacker implements AggregatePacker {
|
||||
|
||||
public class HessianPacker implements Packer {
|
||||
@Override
|
||||
public String pack(ClassPackerConfig classPackerConfig) {
|
||||
return Packers.HessianXSLTScriptEngine.getInstance().pack(classPackerConfig);
|
||||
}
|
||||
}
|
||||
|
||||
+9
-4
@@ -1,11 +1,16 @@
|
||||
package com.reajason.javaweb.packer.deserialize.java;
|
||||
|
||||
import com.reajason.javaweb.packer.AggregatePacker;
|
||||
import com.reajason.javaweb.packer.ClassPackerConfig;
|
||||
import com.reajason.javaweb.packer.Packer;
|
||||
import com.reajason.javaweb.packer.Packers;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2024/12/10
|
||||
*/
|
||||
public class JavaDeserializePacker implements AggregatePacker {
|
||||
|
||||
}
|
||||
public class JavaDeserializePacker implements Packer {
|
||||
@Override
|
||||
public String pack(ClassPackerConfig classPackerConfig) {
|
||||
return Packers.JavaCommonsBeanutils19.getInstance().pack(classPackerConfig);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
package com.reajason.javaweb.packer.groovy;
|
||||
|
||||
import com.reajason.javaweb.packer.AggregatePacker;
|
||||
import com.reajason.javaweb.packer.ClassPackerConfig;
|
||||
import com.reajason.javaweb.packer.Packer;
|
||||
import com.reajason.javaweb.packer.Packers;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2024/12/13
|
||||
*/
|
||||
public class GroovyPacker implements AggregatePacker {
|
||||
|
||||
public class GroovyPacker implements Packer {
|
||||
@Override
|
||||
public String pack(ClassPackerConfig classPackerConfig) {
|
||||
return Packers.GroovyClassDefiner.getInstance().pack(classPackerConfig);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
package com.reajason.javaweb.packer.h2;
|
||||
|
||||
import com.reajason.javaweb.packer.AggregatePacker;
|
||||
import com.reajason.javaweb.packer.ClassPackerConfig;
|
||||
import com.reajason.javaweb.packer.Packer;
|
||||
import com.reajason.javaweb.packer.Packers;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2025/6/28
|
||||
*/
|
||||
public class H2Packer implements AggregatePacker {
|
||||
|
||||
public class H2Packer implements Packer {
|
||||
@Override
|
||||
public String pack(ClassPackerConfig classPackerConfig) {
|
||||
return Packers.H2Javac.getInstance().pack(classPackerConfig);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,8 @@ public class DefaultJarPacker implements JarPacker {
|
||||
|
||||
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
||||
try (JarOutputStream targetJar = new JarOutputStream(byteArrayOutputStream, manifest)) {
|
||||
for (Map.Entry<String, byte[]> entry : jarPackerConfig.getClassBytes().entrySet()) {
|
||||
Map<String, byte[]> classBytes = ((Map<String, byte[]>) jarPackerConfig.getClassBytes());
|
||||
for (Map.Entry<String, byte[]> entry : classBytes.entrySet()) {
|
||||
targetJar.putNextEntry(new JarEntry(entry.getKey().replace('.', '/') + ".class"));
|
||||
targetJar.write(entry.getValue());
|
||||
targetJar.closeEntry();
|
||||
|
||||
@@ -8,6 +8,7 @@ import lombok.SneakyThrows;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Map;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarOutputStream;
|
||||
import java.util.jar.Manifest;
|
||||
@@ -21,7 +22,7 @@ public class GroovyTransformJarPacker implements JarPacker {
|
||||
@SneakyThrows
|
||||
public byte[] packBytes(JarPackerConfig config) {
|
||||
String mainClassName = config.getMainClassName();
|
||||
byte[] mainClassBytes = config.getClassBytes().get(mainClassName);
|
||||
byte[] mainClassBytes = ((Map<String, byte[]>) config.getClassBytes()).get(mainClassName);
|
||||
mainClassBytes = ClassInterfaceUtils.addInterface(mainClassBytes, "org.codehaus.groovy.transform.ASTTransformation");
|
||||
mainClassBytes = ClassAnnotationUtils.setAnnotation(mainClassBytes, "org.codehaus.groovy.transform.GroovyASTTransformation");
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import lombok.SneakyThrows;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Map;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarOutputStream;
|
||||
import java.util.jar.Manifest;
|
||||
@@ -21,7 +22,7 @@ public class ScriptEngineJarPacker implements JarPacker {
|
||||
@SneakyThrows
|
||||
public byte[] packBytes(JarPackerConfig config) {
|
||||
String mainClassName = config.getMainClassName();
|
||||
byte[] mainClassBytes = config.getClassBytes().get(mainClassName);
|
||||
byte[] mainClassBytes = ((Map<String, byte[]>) config.getClassBytes()).get(mainClassName);
|
||||
byte[] bytes = ClassInterfaceUtils.addInterface(mainClassBytes, "javax.script.ScriptEngineFactory");
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
try (JarOutputStream targetJar = new JarOutputStream(outputStream, new Manifest())) {
|
||||
|
||||
@@ -3,20 +3,31 @@ package com.reajason.javaweb.packer.jsp;
|
||||
import com.reajason.javaweb.packer.ClassPackerConfig;
|
||||
import com.reajason.javaweb.packer.Packer;
|
||||
import com.reajason.javaweb.packer.Util;
|
||||
import com.reajason.javaweb.packer.spec.PackerSchema;
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2024/11/26
|
||||
*/
|
||||
public class ClassLoaderJspPacker implements Packer {
|
||||
public class ClassLoaderJspPacker implements Packer<JspCustomPackerConfig> {
|
||||
|
||||
private final String jspTemplate = Util.loadTemplateFromResource("/memshell-party/shell.jsp");
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public String pack(ClassPackerConfig config) {
|
||||
return jspTemplate.replace("{{className}}", config.getClassName())
|
||||
public String pack(ClassPackerConfig<JspCustomPackerConfig> config) {
|
||||
String content = jspTemplate.replace("{{className}}", config.getClassName())
|
||||
.replace("{{base64Str}}", config.getClassBytesBase64Str());
|
||||
JspCustomPackerConfig customConfig = config.getCustomConfig();
|
||||
if (customConfig != null && customConfig.isUnicode()) {
|
||||
return JspUnicoder.encode(content, true);
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PackerSchema schema() {
|
||||
return JspCustomPackerConfig.schema();
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
package com.reajason.javaweb.packer.jsp;
|
||||
|
||||
import com.reajason.javaweb.packer.ClassPackerConfig;
|
||||
import com.reajason.javaweb.packer.Packer;
|
||||
import com.reajason.javaweb.packer.Packers;
|
||||
|
||||
public class ClassLoaderJspUnicodePacker implements Packer {
|
||||
@Override
|
||||
public String pack(ClassPackerConfig classPackerConfig) {
|
||||
return JspUnicoder.encode(Packers.ClassLoaderJSP.getInstance().pack(classPackerConfig), true);
|
||||
}
|
||||
}
|
||||
@@ -3,27 +3,38 @@ package com.reajason.javaweb.packer.jsp;
|
||||
import com.reajason.javaweb.packer.ClassPackerConfig;
|
||||
import com.reajason.javaweb.packer.Packer;
|
||||
import com.reajason.javaweb.packer.Util;
|
||||
import com.reajason.javaweb.packer.spec.PackerSchema;
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2024/11/26
|
||||
*/
|
||||
public class DefineClassJspPacker implements Packer {
|
||||
public class DefineClassJspPacker implements Packer<JspCustomPackerConfig> {
|
||||
|
||||
private final String template = Util.loadTemplateFromResource("/memshell-party/shell1.jsp");
|
||||
private final String bypassTemplate = Util.loadTemplateFromResource("/memshell-party/shell2.jsp");
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public String pack(ClassPackerConfig config) {
|
||||
public String pack(ClassPackerConfig<JspCustomPackerConfig> config) {
|
||||
String injectorBytesBase64Str = config.getClassBytesBase64Str();
|
||||
String injectorClassName = config.getClassName();
|
||||
String template = this.template;
|
||||
if (config.isByPassJavaModule()) {
|
||||
template = bypassTemplate;
|
||||
}
|
||||
return template.replace("{{className}}", injectorClassName)
|
||||
String content = template.replace("{{className}}", injectorClassName)
|
||||
.replace("{{base64Str}}", injectorBytesBase64Str);
|
||||
JspCustomPackerConfig customConfig = config.getCustomConfig();
|
||||
if (customConfig != null && customConfig.isUnicode()) {
|
||||
return JspUnicoder.encode(content, true);
|
||||
}
|
||||
return content;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public PackerSchema schema() {
|
||||
return JspCustomPackerConfig.schema();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
package com.reajason.javaweb.packer.jsp;
|
||||
|
||||
import com.reajason.javaweb.packer.ClassPackerConfig;
|
||||
import com.reajason.javaweb.packer.Packer;
|
||||
import com.reajason.javaweb.packer.Packers;
|
||||
import com.reajason.javaweb.packer.Util;
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2024/11/26
|
||||
*/
|
||||
public class DefineClassJspUnicodePacker implements Packer {
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public String pack(ClassPackerConfig config) {
|
||||
return JspUnicoder.encode(Packers.DefineClassJSP.getInstance().pack(config), true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.reajason.javaweb.packer.jsp;
|
||||
|
||||
import com.reajason.javaweb.packer.spec.PackerFieldSchema;
|
||||
import com.reajason.javaweb.packer.spec.PackerFieldType;
|
||||
import com.reajason.javaweb.packer.spec.PackerSchema;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2026/2/26
|
||||
*/
|
||||
@Data
|
||||
public class JspCustomPackerConfig {
|
||||
private boolean unicode;
|
||||
|
||||
public static PackerSchema schema() {
|
||||
PackerFieldSchema unicodeField = new PackerFieldSchema();
|
||||
unicodeField.setKey("unicode");
|
||||
unicodeField.setType(PackerFieldType.BOOLEAN);
|
||||
unicodeField.setRequired(false);
|
||||
unicodeField.setDefaultValue(false);
|
||||
unicodeField.setDescription("Enable Unicode encoding");
|
||||
unicodeField.setDescriptionI18nKey("unicodeEncoded.desc");
|
||||
|
||||
PackerSchema schema = new PackerSchema();
|
||||
schema.getFields().add(unicodeField);
|
||||
schema.getDefaultConfig().put("unicode", false);
|
||||
return schema;
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,16 @@
|
||||
package com.reajason.javaweb.packer.jsp;
|
||||
|
||||
import com.reajason.javaweb.packer.AggregatePacker;
|
||||
import com.reajason.javaweb.packer.ClassPackerConfig;
|
||||
import com.reajason.javaweb.packer.Packer;
|
||||
import com.reajason.javaweb.packer.Packers;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2024/11/26
|
||||
*/
|
||||
public class JspPacker implements AggregatePacker {
|
||||
}
|
||||
public class JspPacker implements Packer<JspCustomPackerConfig> {
|
||||
@Override
|
||||
public String pack(ClassPackerConfig<JspCustomPackerConfig> classPackerConfig) {
|
||||
return Packers.ClassLoaderJSP.getInstance().pack(classPackerConfig);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,20 +3,31 @@ package com.reajason.javaweb.packer.jsp;
|
||||
import com.reajason.javaweb.packer.ClassPackerConfig;
|
||||
import com.reajason.javaweb.packer.Packer;
|
||||
import com.reajason.javaweb.packer.Util;
|
||||
import com.reajason.javaweb.packer.spec.PackerSchema;
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2024/11/26
|
||||
*/
|
||||
public class JspxPacker implements Packer {
|
||||
public class JspxPacker implements Packer<JspCustomPackerConfig> {
|
||||
|
||||
private final String jspxTemplate = Util.loadTemplateFromResource("/memshell-party/shell.jspx");
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public String pack(ClassPackerConfig config) {
|
||||
return jspxTemplate.replace("{{className}}", config.getClassName())
|
||||
public String pack(ClassPackerConfig<JspCustomPackerConfig> config) {
|
||||
String content = jspxTemplate.replace("{{className}}", config.getClassName())
|
||||
.replace("{{base64Str}}", config.getClassBytesBase64Str());
|
||||
JspCustomPackerConfig customConfig = config.getCustomConfig();
|
||||
if (customConfig != null && customConfig.isUnicode()) {
|
||||
return JspUnicoder.encode(content, false);
|
||||
}
|
||||
return content;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public PackerSchema schema() {
|
||||
return JspCustomPackerConfig.schema();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
package com.reajason.javaweb.packer.jsp;
|
||||
|
||||
import com.reajason.javaweb.packer.ClassPackerConfig;
|
||||
import com.reajason.javaweb.packer.Packer;
|
||||
import com.reajason.javaweb.packer.Packers;
|
||||
|
||||
public class JspxUnicodePacker implements Packer {
|
||||
@Override
|
||||
public String pack(ClassPackerConfig classPackerConfig) {
|
||||
String content = Packers.JSPX.getInstance().pack(classPackerConfig);
|
||||
return JspUnicoder.encode(content, false);
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,16 @@
|
||||
package com.reajason.javaweb.packer.jxpath;
|
||||
|
||||
import com.reajason.javaweb.packer.AggregatePacker;
|
||||
import com.reajason.javaweb.packer.ClassPackerConfig;
|
||||
import com.reajason.javaweb.packer.Packer;
|
||||
import com.reajason.javaweb.packer.Packers;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2024/12/13
|
||||
*/
|
||||
public class JXPathPacker implements AggregatePacker {
|
||||
|
||||
public class JXPathPacker implements Packer {
|
||||
@Override
|
||||
public String pack(ClassPackerConfig classPackerConfig) {
|
||||
return Packers.JXPathScriptEngine.getInstance().pack(classPackerConfig);
|
||||
}
|
||||
}
|
||||
|
||||
+3
-1
@@ -3,6 +3,8 @@ package com.reajason.javaweb.packer.jxpath;
|
||||
import com.reajason.javaweb.packer.ClassPackerConfig;
|
||||
import com.reajason.javaweb.packer.Packer;
|
||||
import com.reajason.javaweb.packer.Packers;
|
||||
import com.reajason.javaweb.packer.Util;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
|
||||
import static com.reajason.javaweb.packer.spel.SpELSpringGzipJDK17Packer.assertClassNameValid;
|
||||
|
||||
@@ -18,6 +20,6 @@ public class JXPathSpringGzipJDK17Packer implements Packer {
|
||||
String className = config.getClassName();
|
||||
assertClassNameValid(className);
|
||||
return template.replace("{{className}}", className)
|
||||
.replace("{{base64Str}}", Packers.GzipBase64.getInstance().pack(config));
|
||||
.replace("{{base64Str}}", Base64.encodeBase64String(Util.gzipCompress(config.getClassBytes())));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ package com.reajason.javaweb.packer.jxpath;
|
||||
import com.reajason.javaweb.packer.ClassPackerConfig;
|
||||
import com.reajason.javaweb.packer.Packer;
|
||||
import com.reajason.javaweb.packer.Packers;
|
||||
import com.reajason.javaweb.packer.Util;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
@@ -14,6 +16,6 @@ public class JXPathSpringGzipPacker implements Packer {
|
||||
@Override
|
||||
public String pack(ClassPackerConfig config) {
|
||||
return template.replace("{{className}}", config.getClassName())
|
||||
.replace("{{base64Str}}", Packers.GzipBase64.getInstance().pack(config));
|
||||
.replace("{{base64Str}}", Base64.encodeBase64String(Util.gzipCompress(config.getClassBytes())));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
package com.reajason.javaweb.packer.ognl;
|
||||
|
||||
import com.reajason.javaweb.packer.AggregatePacker;
|
||||
import com.reajason.javaweb.packer.ClassPackerConfig;
|
||||
import com.reajason.javaweb.packer.Packer;
|
||||
import com.reajason.javaweb.packer.Packers;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2024/12/14
|
||||
*/
|
||||
public class OGNLPacker implements AggregatePacker {
|
||||
|
||||
public class OGNLPacker implements Packer {
|
||||
@Override
|
||||
public String pack(ClassPackerConfig classPackerConfig) {
|
||||
return Packers.OGNLScriptEngine.getInstance().pack(classPackerConfig);
|
||||
}
|
||||
}
|
||||
|
||||
+3
-1
@@ -3,7 +3,9 @@ package com.reajason.javaweb.packer.ognl;
|
||||
import com.reajason.javaweb.packer.ClassPackerConfig;
|
||||
import com.reajason.javaweb.packer.Packer;
|
||||
import com.reajason.javaweb.packer.Packers;
|
||||
import com.reajason.javaweb.packer.Util;
|
||||
import lombok.SneakyThrows;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
|
||||
import static com.reajason.javaweb.packer.spel.SpELSpringGzipJDK17Packer.assertClassNameValid;
|
||||
|
||||
@@ -20,6 +22,6 @@ public class OGNLSpringGzipJDK17Packer implements Packer {
|
||||
String className = config.getClassName();
|
||||
assertClassNameValid(className);
|
||||
return template.replace("{{className}}", className)
|
||||
.replace("{{base64Str}}", Packers.GzipBase64.getInstance().pack(config));
|
||||
.replace("{{base64Str}}", Base64.encodeBase64String(Util.gzipCompress(config.getClassBytes())));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,9 @@ package com.reajason.javaweb.packer.ognl;
|
||||
import com.reajason.javaweb.packer.ClassPackerConfig;
|
||||
import com.reajason.javaweb.packer.Packer;
|
||||
import com.reajason.javaweb.packer.Packers;
|
||||
import com.reajason.javaweb.packer.Util;
|
||||
import lombok.SneakyThrows;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
@@ -16,6 +18,6 @@ public class OGNLSpringGzipPacker implements Packer {
|
||||
@SneakyThrows
|
||||
public String pack(ClassPackerConfig config) {
|
||||
return template.replace("{{className}}", config.getClassName())
|
||||
.replace("{{base64Str}}", Packers.GzipBase64.getInstance().pack(config));
|
||||
.replace("{{base64Str}}", Base64.encodeBase64String(Util.gzipCompress(config.getClassBytes())));
|
||||
}
|
||||
}
|
||||
|
||||
+8
-2
@@ -1,10 +1,16 @@
|
||||
package com.reajason.javaweb.packer.scriptengine;
|
||||
|
||||
import com.reajason.javaweb.packer.AggregatePacker;
|
||||
import com.reajason.javaweb.packer.ClassPackerConfig;
|
||||
import com.reajason.javaweb.packer.Packer;
|
||||
import com.reajason.javaweb.packer.Packers;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2024/12/3
|
||||
*/
|
||||
public class ScriptEnginePacker implements AggregatePacker {
|
||||
public class ScriptEnginePacker implements Packer {
|
||||
@Override
|
||||
public String pack(ClassPackerConfig classPackerConfig) {
|
||||
return Packers.DefaultScriptEngine.getInstance().pack(classPackerConfig);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.reajason.javaweb.packer.spec;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Field metadata for packer config UI.
|
||||
*/
|
||||
@Data
|
||||
public class PackerFieldSchema {
|
||||
private String key;
|
||||
private PackerFieldType type;
|
||||
private boolean required;
|
||||
private Object defaultValue;
|
||||
private String description;
|
||||
private String descriptionI18nKey;
|
||||
private List<PackerOptionValue> options = new ArrayList<>();
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.reajason.javaweb.packer.spec;
|
||||
|
||||
/**
|
||||
* Supported packer config field types.
|
||||
*/
|
||||
public enum PackerFieldType {
|
||||
BOOLEAN,
|
||||
STRING,
|
||||
ENUM,
|
||||
INTEGER
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.reajason.javaweb.packer.spec;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* Enum option metadata for UI rendering.
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class PackerOptionValue {
|
||||
private String value;
|
||||
private String label;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.reajason.javaweb.packer.spec;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Request-time packer selection and config.
|
||||
*/
|
||||
@Data
|
||||
public class PackerRequestSpec {
|
||||
private String name;
|
||||
private Map<String, Object> config = new LinkedHashMap<>();
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.reajason.javaweb.packer.spec;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Packer config schema metadata.
|
||||
*/
|
||||
@Data
|
||||
public class PackerSchema {
|
||||
private List<PackerFieldSchema> fields = new ArrayList<>();
|
||||
private Map<String, Object> defaultConfig = new LinkedHashMap<>();
|
||||
|
||||
public static PackerSchema empty() {
|
||||
return new PackerSchema();
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,16 @@
|
||||
package com.reajason.javaweb.packer.spel;
|
||||
|
||||
import com.reajason.javaweb.packer.AggregatePacker;
|
||||
import com.reajason.javaweb.packer.ClassPackerConfig;
|
||||
import com.reajason.javaweb.packer.Packer;
|
||||
import com.reajason.javaweb.packer.Packers;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2025/1/26
|
||||
*/
|
||||
public class SpELPacker implements AggregatePacker {
|
||||
public class SpELPacker implements Packer {
|
||||
@Override
|
||||
public String pack(ClassPackerConfig classPackerConfig) {
|
||||
return Packers.SpELScriptEngine.getInstance().pack(classPackerConfig);
|
||||
}
|
||||
}
|
||||
|
||||
+3
-1
@@ -3,6 +3,8 @@ package com.reajason.javaweb.packer.spel;
|
||||
import com.reajason.javaweb.packer.ClassPackerConfig;
|
||||
import com.reajason.javaweb.packer.Packer;
|
||||
import com.reajason.javaweb.packer.Packers;
|
||||
import com.reajason.javaweb.packer.Util;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
@@ -16,7 +18,7 @@ public class SpELSpringGzipJDK17Packer implements Packer {
|
||||
String className = config.getClassName();
|
||||
assertClassNameValid(className);
|
||||
return template.replace("{{className}}", className)
|
||||
.replace("{{base64Str}}", Packers.GzipBase64.getInstance().pack(config));
|
||||
.replace("{{base64Str}}", Base64.encodeBase64String(Util.gzipCompress(config.getClassBytes())));
|
||||
}
|
||||
|
||||
public static void assertClassNameValid(String className) {
|
||||
|
||||
@@ -3,6 +3,8 @@ package com.reajason.javaweb.packer.spel;
|
||||
import com.reajason.javaweb.packer.ClassPackerConfig;
|
||||
import com.reajason.javaweb.packer.Packer;
|
||||
import com.reajason.javaweb.packer.Packers;
|
||||
import com.reajason.javaweb.packer.Util;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
|
||||
|
||||
/**
|
||||
@@ -19,6 +21,6 @@ public class SpELSpringGzipPacker implements Packer {
|
||||
@Override
|
||||
public String pack(ClassPackerConfig config) {
|
||||
return template.replace("{{className}}", config.getClassName())
|
||||
.replace("{{base64Str}}", Packers.GzipBase64.getInstance().pack(config));
|
||||
.replace("{{base64Str}}", Base64.encodeBase64String(Util.gzipCompress(config.getClassBytes())));
|
||||
}
|
||||
}
|
||||
+8
-2
@@ -1,10 +1,16 @@
|
||||
package com.reajason.javaweb.packer.translet;
|
||||
|
||||
import com.reajason.javaweb.packer.AggregatePacker;
|
||||
import com.reajason.javaweb.packer.ClassPackerConfig;
|
||||
import com.reajason.javaweb.packer.Packer;
|
||||
import com.reajason.javaweb.packer.Packers;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2025/11/19
|
||||
*/
|
||||
public class AbstractTransletPacker implements AggregatePacker {
|
||||
public class AbstractTransletPacker implements Packer {
|
||||
@Override
|
||||
public String pack(ClassPackerConfig classPackerConfig) {
|
||||
return Packers.JDKAbstractTransletPacker.getInstance().pack(classPackerConfig);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
package com.reajason.javaweb.packer.xmldecoder;
|
||||
|
||||
import com.reajason.javaweb.packer.AggregatePacker;
|
||||
import com.reajason.javaweb.packer.ClassPackerConfig;
|
||||
import com.reajason.javaweb.packer.Packer;
|
||||
import com.reajason.javaweb.packer.Packers;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2025/7/22
|
||||
*/
|
||||
public class XMLDecoderPacker implements AggregatePacker {
|
||||
|
||||
public class XMLDecoderPacker implements Packer {
|
||||
@Override
|
||||
public String pack(ClassPackerConfig classPackerConfig) {
|
||||
return Packers.XMLDecoderScriptEngine.getInstance().pack(classPackerConfig);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
package com.reajason.javaweb.packer;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
class PackerCustomConfigTest {
|
||||
|
||||
@Test
|
||||
void typedPackerShouldInferCustomConfigType() {
|
||||
TypedDemoPacker packer = new TypedDemoPacker();
|
||||
Assertions.assertEquals(DemoCustomConfig.class, packer.customConfigType());
|
||||
}
|
||||
|
||||
@Test
|
||||
void rawPackerShouldTreatAsNoCustomConfig() {
|
||||
RawDemoPacker packer = new RawDemoPacker();
|
||||
Assertions.assertNull(packer.customConfigType());
|
||||
Assertions.assertNull(packer.resolveCustomConfig(Map.of("enabled", true)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void typedPackerShouldResolveCustomConfig() {
|
||||
TypedDemoPacker packer = new TypedDemoPacker();
|
||||
DemoCustomConfig resolved = packer.resolveCustomConfig(Map.of(
|
||||
"name", "demo",
|
||||
"enabled", true,
|
||||
"count", 7
|
||||
));
|
||||
Assertions.assertEquals("demo", resolved.getName());
|
||||
Assertions.assertTrue(resolved.isEnabled());
|
||||
Assertions.assertEquals(7, resolved.getCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
void typedPackerShouldReturnDefaultWhenNull() {
|
||||
TypedDemoPackerWithDefault packer = new TypedDemoPackerWithDefault();
|
||||
DemoCustomConfig resolved = packer.resolveCustomConfig(null);
|
||||
Assertions.assertNotNull(resolved);
|
||||
Assertions.assertEquals("default", resolved.getName());
|
||||
Assertions.assertEquals(9, resolved.getCount());
|
||||
}
|
||||
|
||||
static class TypedDemoPacker implements Packer<DemoCustomConfig> {
|
||||
}
|
||||
|
||||
static class TypedDemoPackerWithDefault implements Packer<DemoCustomConfig> {
|
||||
@Override
|
||||
public DemoCustomConfig defaultCustomConfig() {
|
||||
DemoCustomConfig config = new DemoCustomConfig();
|
||||
config.setName("default");
|
||||
config.setCount(9);
|
||||
return config;
|
||||
}
|
||||
}
|
||||
|
||||
static class RawDemoPacker implements Packer {
|
||||
}
|
||||
|
||||
public static class DemoCustomConfig {
|
||||
private String name;
|
||||
private boolean enabled;
|
||||
private int count;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
return count;
|
||||
}
|
||||
|
||||
public void setCount(int count) {
|
||||
this.count = count;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package com.reajason.javaweb.packer.jsp;
|
||||
|
||||
import com.reajason.javaweb.packer.ClassPackerConfig;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
class JspCustomPackerConfigTest {
|
||||
|
||||
@Test
|
||||
void classLoaderJspShouldApplyUnicodeWhenEnabled() {
|
||||
ClassLoaderJspPacker packer = new ClassLoaderJspPacker();
|
||||
ClassPackerConfig<JspCustomPackerConfig> config = buildConfig();
|
||||
config.setCustomConfig(packer.resolveCustomConfig(null));
|
||||
|
||||
String plain = packer.pack(config);
|
||||
config.setCustomConfig(packer.resolveCustomConfig(Collections.singletonMap("unicode", true)));
|
||||
String unicode = packer.pack(config);
|
||||
|
||||
Assertions.assertEquals(JspUnicoder.encode(plain, true), unicode);
|
||||
}
|
||||
|
||||
@Test
|
||||
void defineClassJspShouldApplyUnicodeWhenEnabled() {
|
||||
DefineClassJspPacker packer = new DefineClassJspPacker();
|
||||
ClassPackerConfig<JspCustomPackerConfig> config = buildConfig();
|
||||
config.setCustomConfig(packer.resolveCustomConfig(null));
|
||||
|
||||
String plain = packer.pack(config);
|
||||
config.setCustomConfig(packer.resolveCustomConfig(Collections.singletonMap("unicode", true)));
|
||||
String unicode = packer.pack(config);
|
||||
|
||||
Assertions.assertEquals(JspUnicoder.encode(plain, true), unicode);
|
||||
}
|
||||
|
||||
@Test
|
||||
void jspxShouldApplyUnicodeWhenEnabled() {
|
||||
JspxPacker packer = new JspxPacker();
|
||||
ClassPackerConfig<JspCustomPackerConfig> config = buildConfig();
|
||||
config.setCustomConfig(packer.resolveCustomConfig(null));
|
||||
|
||||
String plain = packer.pack(config);
|
||||
config.setCustomConfig(packer.resolveCustomConfig(Collections.singletonMap("unicode", true)));
|
||||
String unicode = packer.pack(config);
|
||||
|
||||
Assertions.assertEquals(JspUnicoder.encode(plain, false), unicode);
|
||||
}
|
||||
|
||||
@Test
|
||||
void defaultShouldDisableUnicode() {
|
||||
ClassLoaderJspPacker packer = new ClassLoaderJspPacker();
|
||||
ClassPackerConfig<JspCustomPackerConfig> config = buildConfig();
|
||||
config.setCustomConfig(packer.resolveCustomConfig(null));
|
||||
String plain = packer.pack(config);
|
||||
|
||||
config.setCustomConfig(packer.resolveCustomConfig(Collections.emptyMap()));
|
||||
Assertions.assertEquals(plain, packer.pack(config));
|
||||
}
|
||||
|
||||
@Test
|
||||
void invalidUnicodeTypeShouldThrow() {
|
||||
ClassLoaderJspPacker packer = new ClassLoaderJspPacker();
|
||||
Assertions.assertThrows(IllegalArgumentException.class,
|
||||
() -> packer.resolveCustomConfig(Collections.singletonMap("unicode", "oops")));
|
||||
}
|
||||
|
||||
private static ClassPackerConfig<JspCustomPackerConfig> buildConfig() {
|
||||
ClassPackerConfig<JspCustomPackerConfig> config = new ClassPackerConfig<>();
|
||||
config.setClassName("hello.world.Injector");
|
||||
config.setClassBytesBase64Str("QUJDRA==");
|
||||
config.setClassBytes(new byte[]{1, 2, 3, 4});
|
||||
return config;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user