feat: support i18n [skip ci]

This commit is contained in:
ReaJason
2025-01-20 00:34:10 +08:00
parent f78c83d901
commit 9154b1b46c
19 changed files with 578 additions and 215 deletions
@@ -10,7 +10,10 @@ import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import java.util.*; import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
@@ -48,13 +51,7 @@ public class ConfigController {
.collect(Collectors.toList()) .collect(Collectors.toList())
); );
config.setCore(coreMap); config.setCore(coreMap);
config.setPackers(Arrays.stream(Packer.INSTANCE.values()) config.setPackers(Arrays.stream(Packer.INSTANCE.values()).map(Packer.INSTANCE::name).toList());
.collect(Collectors.toMap(
Packer.INSTANCE::getDesc,
Packer.INSTANCE::name,
(e1, e2) -> e1,
LinkedHashMap::new
)));
return ResponseEntity.ok(config); return ResponseEntity.ok(config);
} }
} }
@@ -13,5 +13,5 @@ import java.util.Map;
public class Config { public class Config {
private List<String> servers; private List<String> servers;
private Map<String, Map<?, ?>> core; private Map<String, Map<?, ?>> core;
private Map<String, String> packers; private List<String> packers;
} }
@@ -42,49 +42,46 @@ public interface Packer {
/** /**
* Base64 * Base64
*/ */
Base64("Base64", new Base64Packer()), Base64(new Base64Packer()),
/** /**
* BCEL * BCEL
*/ */
BCEL("BCEL", new BCELPacker()), BCEL(new BCELPacker()),
/** /**
* JSP 打包器 * JSP 打包器
*/ */
JSP("JSP", new JspPacker()), JSP(new JspPacker()),
/** /**
* 脚本引擎打包器 * 脚本引擎打包器
*/ */
ScriptEngine("脚本引擎", new ScriptEnginePacker()), ScriptEngine(new ScriptEnginePacker()),
/** /**
* 反序列化打包器 * 反序列化打包器
*/ */
Deserialize("反序列化(Only CB4, 1.9.x)", new DeserializePacker()), Deserialize(new DeserializePacker()),
/** /**
* EL * EL
*/ */
EL("EL 表达式", new ELPacker()), EL(new ELPacker()),
OGNL("OGNL 表达式", new OGNLPacker()), OGNL(new OGNLPacker()),
SpEL("SpEL 表达式", new SpELPacker()), SpEL(new SpELPacker()),
Freemarker("Freemarker", new FreemarkerPacker()), Freemarker(new FreemarkerPacker()),
Velocity("Velocity", new VelocityPacker()), Velocity(new VelocityPacker()),
AgentJar("AgentJar", new AgentJarPacker()), AgentJar(new AgentJarPacker()),
; ;
private final String desc;
private final Packer packer; private final Packer packer;
INSTANCE(String desc, Packer packer) { INSTANCE(Packer packer) {
this.desc = desc;
this.packer = packer; this.packer = packer;
} }
} }
BIN
View File
Binary file not shown.
+6 -4
View File
@@ -13,7 +13,7 @@
}, },
"devDependencies": { "devDependencies": {
"@biomejs/biome": "1.9.4", "@biomejs/biome": "1.9.4",
"@tanstack/router-plugin": "^1.97.1", "@tanstack/router-plugin": "^1.97.3",
"@types/node": "^22.10.7", "@types/node": "^22.10.7",
"@types/react": "^19.0.7", "@types/react": "^19.0.7",
"@types/react-dom": "^19.0.3", "@types/react-dom": "^19.0.3",
@@ -39,17 +39,19 @@
"@radix-ui/react-switch": "^1.1.2", "@radix-ui/react-switch": "^1.1.2",
"@radix-ui/react-tabs": "^1.1.2", "@radix-ui/react-tabs": "^1.1.2",
"@radix-ui/react-tooltip": "^1.1.6", "@radix-ui/react-tooltip": "^1.1.6",
"@tanstack/react-query": "^5.64.1", "@tanstack/react-query": "^5.64.2",
"@tanstack/react-router": "^1.97.1", "@tanstack/react-router": "^1.97.3",
"@tanstack/router-devtools": "^1.97.1", "@tanstack/router-devtools": "^1.97.3",
"class-variance-authority": "^0.7.1", "class-variance-authority": "^0.7.1",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"i18next": "^24.2.1",
"lucide-react": "^0.473.0", "lucide-react": "^0.473.0",
"next-themes": "^0.4.4", "next-themes": "^0.4.4",
"react": "^19.0.0", "react": "^19.0.0",
"react-copy-to-clipboard": "^5.1.0", "react-copy-to-clipboard": "^5.1.0",
"react-dom": "^19.0.0", "react-dom": "^19.0.0",
"react-hook-form": "^7.54.2", "react-hook-form": "^7.54.2",
"react-i18next": "^15.4.0",
"react-syntax-highlighter": "^15.6.1", "react-syntax-highlighter": "^15.6.1",
"sonner": "^1.7.2", "sonner": "^1.7.2",
"tailwind-merge": "^2.6.0", "tailwind-merge": "^2.6.0",
+23
View File
@@ -0,0 +1,23 @@
import { LanguagesIcon } from "lucide-react";
import { useTranslation } from "react-i18next";
import { Button } from "./ui/button";
export function LanguageSwitcher() {
const { i18n } = useTranslation();
const toggleLanguage = () => {
const newLang = i18n.language === "en" ? "zh" : "en";
i18n.changeLanguage(newLang);
};
return (
<Button
variant="ghost"
size="icon"
onClick={toggleLanguage}
title={i18n.language === "en" ? "Switch to Chinese" : "切换到英文"}
>
<LanguagesIcon className="h-5 w-5" />
</Button>
);
}
+102 -37
View File
@@ -14,6 +14,7 @@ import { cn } from "@/lib/utils.ts";
import { ArrowUpRightIcon, ServerIcon } from "lucide-react"; import { ArrowUpRightIcon, ServerIcon } from "lucide-react";
import { useState } from "react"; import { useState } from "react";
import { FormProvider, UseFormReturn } from "react-hook-form"; import { FormProvider, UseFormReturn } from "react-hook-form";
import { useTranslation } from "react-i18next";
const JDKVersion = [ const JDKVersion = [
{ name: "Java6", value: "50" }, { name: "Java6", value: "50" },
@@ -34,9 +35,17 @@ export function MainConfigCard({
servers?: string[]; servers?: string[];
}) { }) {
const [shellToolMap, setShellToolMap] = useState<{ [toolName: string]: string[] }>(); const [shellToolMap, setShellToolMap] = useState<{ [toolName: string]: string[] }>();
const [shellTools, setShellTools] = useState<string[]>(["Behinder", "Godzilla", "Command"]); const [shellTools, setShellTools] = useState<string[]>([
"Behinder",
"Godzilla",
"Command",
"AntSword",
"Suo5",
"Neo-reGeorg",
]);
const [shellTypes, setShellTypes] = useState<string[]>([]); const [shellTypes, setShellTypes] = useState<string[]>([]);
const shellTool = form.watch("shellTool"); const shellTool = form.watch("shellTool");
const { t } = useTranslation();
const handleServerChange = (value: string) => { const handleServerChange = (value: string) => {
if (mainConfig) { if (mainConfig) {
@@ -97,7 +106,7 @@ export function MainConfigCard({
<CardHeader className="pb-1"> <CardHeader className="pb-1">
<CardTitle className="text-md flex items-center gap-2"> <CardTitle className="text-md flex items-center gap-2">
<ServerIcon className="h-5" /> <ServerIcon className="h-5" />
<span>生成配置</span> <span>{t("configs.main-config")}</span>
</CardTitle> </CardTitle>
</CardHeader> </CardHeader>
<CardContent> <CardContent>
@@ -107,7 +116,7 @@ export function MainConfigCard({
name="server" name="server"
render={({ field }) => ( render={({ field }) => (
<FormItem className="space-y-1"> <FormItem className="space-y-1">
<FormLabel>目标服务</FormLabel> <FormLabel>{t("mainConfig.server")}</FormLabel>
<Select <Select
onValueChange={(v) => { onValueChange={(v) => {
field.onChange(v); field.onChange(v);
@@ -117,7 +126,7 @@ export function MainConfigCard({
> >
<FormControl> <FormControl>
<SelectTrigger className="h-8"> <SelectTrigger className="h-8">
<SelectValue placeholder="请选择" /> <SelectValue placeholder={t("placeholders.select")} />
</SelectTrigger> </SelectTrigger>
</FormControl> </FormControl>
<SelectContent> <SelectContent>
@@ -129,14 +138,14 @@ export function MainConfigCard({
</SelectContent> </SelectContent>
</Select> </Select>
<FormDescription className="flex items-center"> <FormDescription className="flex items-center">
下拉列表找不到目标服务 ? {t("tips.targetServerNotFound")}&nbsp;
<a <a
href="https://github.com/ReaJason/MemShellParty/issues/new?template=%E8%AF%B7%E6%B1%82%E9%80%82%E9%85%8D.md" href="https://github.com/ReaJason/MemShellParty/issues/new?template=%E8%AF%B7%E6%B1%82%E9%80%82%E9%85%8D.md"
target="_blank" target="_blank"
rel="noreferrer" rel="noreferrer"
className="flex items-center underline" className="flex items-center underline"
> >
请求适配 {t("tips.targetServerRequest")}
<ArrowUpRightIcon className="h-4" /> <ArrowUpRightIcon className="h-4" />
</a> </a>
</FormDescription> </FormDescription>
@@ -149,7 +158,7 @@ export function MainConfigCard({
render={({ field }) => ( render={({ field }) => (
<FormItem className="flex flex-col mt-1"> <FormItem className="flex flex-col mt-1">
<Label className="flex items-center"> <Label className="flex items-center">
目标 JRE 版本(可选) <JreTip /> {t("mainConfig.jre")} {t("optional")} <JreTip />
</Label> </Label>
<Select <Select
onValueChange={(v) => { onValueChange={(v) => {
@@ -164,7 +173,7 @@ export function MainConfigCard({
> >
<FormControl> <FormControl>
<SelectTrigger className="h-8"> <SelectTrigger className="h-8">
<SelectValue placeholder="请选择" /> <SelectValue placeholder={t("placeholders.select")} />
</SelectTrigger> </SelectTrigger>
</FormControl> </FormControl>
<SelectContent> <SelectContent>
@@ -188,7 +197,7 @@ export function MainConfigCard({
<FormControl> <FormControl>
<Switch id="debug" checked={field.value} onCheckedChange={field.onChange} /> <Switch id="debug" checked={field.value} onCheckedChange={field.onChange} />
</FormControl> </FormControl>
<FormLabel htmlFor="debug">开启调试</FormLabel> <FormLabel htmlFor="debug">{t("mainConfig.debug")}</FormLabel>
</FormItem> </FormItem>
)} )}
/> />
@@ -200,7 +209,7 @@ export function MainConfigCard({
<FormControl> <FormControl>
<Switch id="bypassJavaModule" checked={field.value} onCheckedChange={field.onChange} /> <Switch id="bypassJavaModule" checked={field.value} onCheckedChange={field.onChange} />
</FormControl> </FormControl>
<Label htmlFor="bypassJavaModule">绕过 Java 模块系统限制</Label> <Label htmlFor="bypassJavaModule">{t("mainConfig.bypassJavaModule")}</Label>
</FormItem> </FormItem>
)} )}
/> />
@@ -214,7 +223,9 @@ export function MainConfigCard({
}} }}
className="w-full" className="w-full"
> >
<TabsList className={cn("grid w-full", `grid-cols-${shellTools.length}`)}> <TabsList
className={cn("grid w-full", shellTools.length > 3 ? "grid-flow-col" : `grid-cols-${shellTools.length}`)}
>
{shellTools.map((shellTool) => ( {shellTools.map((shellTool) => (
<TabsTrigger key={shellTool} value={shellTool}> <TabsTrigger key={shellTool} value={shellTool}>
{shellTool} {shellTool}
@@ -224,12 +235,16 @@ export function MainConfigCard({
<BehinderTabContent form={form} shellTypes={shellTypes} /> <BehinderTabContent form={form} shellTypes={shellTypes} />
<GodzillaTabContent form={form} shellTypes={shellTypes} /> <GodzillaTabContent form={form} shellTypes={shellTypes} />
<CommandTabContent form={form} shellTypes={shellTypes} /> <CommandTabContent form={form} shellTypes={shellTypes} />
<AntSwordTabContent />
<Suo5TabContent />
<NeoreGeorgTabContent />
</Tabs> </Tabs>
</FormProvider> </FormProvider>
); );
} }
function ShellTypeFormField({ form, shellTypes }: { form: UseFormReturn<FormSchema>; shellTypes: Array<string> }) { function ShellTypeFormField({ form, shellTypes }: { form: UseFormReturn<FormSchema>; shellTypes: Array<string> }) {
const { t } = useTranslation();
return ( return (
<FormProvider {...form}> <FormProvider {...form}>
<FormField <FormField
@@ -237,11 +252,11 @@ function ShellTypeFormField({ form, shellTypes }: { form: UseFormReturn<FormSche
name="shellType" name="shellType"
render={({ field }) => ( render={({ field }) => (
<FormItem className="space-y-1"> <FormItem className="space-y-1">
<FormLabel>内存马挂载类型</FormLabel> <FormLabel>{t("mainConfig.shellMountType")}</FormLabel>
<Select onValueChange={field.onChange} value={field.value}> <Select onValueChange={field.onChange} value={field.value}>
<FormControl> <FormControl>
<SelectTrigger className="h-8"> <SelectTrigger className="h-8">
<SelectValue placeholder="请选择" /> <SelectValue placeholder={t("placeholders.select")} />
</SelectTrigger> </SelectTrigger>
</FormControl> </FormControl>
<SelectContent key={shellTypes.join(",")}> <SelectContent key={shellTypes.join(",")}>
@@ -252,7 +267,7 @@ function ShellTypeFormField({ form, shellTypes }: { form: UseFormReturn<FormSche
</SelectItem> </SelectItem>
)) ))
) : ( ) : (
<SelectItem value=" ">请先选择内存马工具类型</SelectItem> <SelectItem value=" ">{t("tips.shellToolNotSelected")}</SelectItem>
)} )}
</SelectContent> </SelectContent>
</Select> </Select>
@@ -264,6 +279,7 @@ function ShellTypeFormField({ form, shellTypes }: { form: UseFormReturn<FormSche
} }
function UrlPatternFormField({ form }: { form: UseFormReturn<FormSchema> }) { function UrlPatternFormField({ form }: { form: UseFormReturn<FormSchema> }) {
const { t } = useTranslation();
return ( return (
<FormProvider {...form}> <FormProvider {...form}>
<FormField <FormField
@@ -272,9 +288,9 @@ function UrlPatternFormField({ form }: { form: UseFormReturn<FormSchema> }) {
render={({ field }) => ( render={({ field }) => (
<FormItem className="flex flex-col mt-1"> <FormItem className="flex flex-col mt-1">
<Label className="flex items-center"> <Label className="flex items-center">
请求路径 <UrlPatternTip /> {t("mainConfig.urlPattern")} <UrlPatternTip />
</Label> </Label>
<Input {...field} placeholder="请输入" className="h-8" /> <Input {...field} placeholder={t("placeholders.input")} className="h-8" />
</FormItem> </FormItem>
)} )}
/> />
@@ -283,6 +299,7 @@ function UrlPatternFormField({ form }: { form: UseFormReturn<FormSchema> }) {
} }
function OptionalClassFormField({ form }: { form: UseFormReturn<FormSchema> }) { function OptionalClassFormField({ form }: { form: UseFormReturn<FormSchema> }) {
const { t } = useTranslation();
return ( return (
<FormProvider {...form}> <FormProvider {...form}>
<FormField <FormField
@@ -290,8 +307,10 @@ function OptionalClassFormField({ form }: { form: UseFormReturn<FormSchema> }) {
name="shellClassName" name="shellClassName"
render={({ field }) => ( render={({ field }) => (
<FormItem className="space-y-1"> <FormItem className="space-y-1">
<FormLabel>内存马类名(可选)</FormLabel> <FormLabel>
<Input id="shellClassName" {...field} placeholder="请输入" className="h-8" /> {t("mainConfig.shellClassName")} {t("optional")}
</FormLabel>
<Input id="shellClassName" {...field} placeholder={t("placeholders.input")} className="h-8" />
</FormItem> </FormItem>
)} )}
/> />
@@ -300,8 +319,10 @@ function OptionalClassFormField({ form }: { form: UseFormReturn<FormSchema> }) {
name="injectorClassName" name="injectorClassName"
render={({ field }) => ( render={({ field }) => (
<FormItem className="space-y-1"> <FormItem className="space-y-1">
<FormLabel>注入器类名(可选)</FormLabel> <FormLabel>
<Input id="injectorClassName" {...field} placeholder="请输入" className="h-8" /> {t("mainConfig.injectorClassName")} {t("optional")}
</FormLabel>
<Input id="injectorClassName" {...field} placeholder={t("placeholders.input")} className="h-8" />
</FormItem> </FormItem>
)} )}
/> />
@@ -310,6 +331,7 @@ function OptionalClassFormField({ form }: { form: UseFormReturn<FormSchema> }) {
} }
function BehinderTabContent({ form, shellTypes }: { form: UseFormReturn<FormSchema>; shellTypes: Array<string> }) { function BehinderTabContent({ form, shellTypes }: { form: UseFormReturn<FormSchema>; shellTypes: Array<string> }) {
const { t } = useTranslation();
return ( return (
<FormProvider {...form}> <FormProvider {...form}>
<TabsContent value="Behinder"> <TabsContent value="Behinder">
@@ -324,8 +346,8 @@ function BehinderTabContent({ form, shellTypes }: { form: UseFormReturn<FormSche
name="behinderPass" name="behinderPass"
render={({ field }) => ( render={({ field }) => (
<FormItem className="space-y-1"> <FormItem className="space-y-1">
<FormLabel>连接密码</FormLabel> <FormLabel>{t("shellToolConfig.behinderPass")}</FormLabel>
<Input {...field} placeholder="Pass" className="h-8" /> <Input {...field} placeholder={t("shellToolConfig.pass")} className="h-8" />
</FormItem> </FormItem>
)} )}
/> />
@@ -335,8 +357,8 @@ function BehinderTabContent({ form, shellTypes }: { form: UseFormReturn<FormSche
name="behinderHeaderName" name="behinderHeaderName"
render={({ field }) => ( render={({ field }) => (
<FormItem className="space-y-1"> <FormItem className="space-y-1">
<FormLabel>请求头键</FormLabel> <FormLabel>{t("shellToolConfig.headerName")}</FormLabel>
<Input {...field} placeholder="Header Name" className="h-8" /> <Input {...field} placeholder={t("shellToolConfig.headerName")} className="h-8" />
</FormItem> </FormItem>
)} )}
/> />
@@ -345,8 +367,8 @@ function BehinderTabContent({ form, shellTypes }: { form: UseFormReturn<FormSche
name="behinderHeaderValue" name="behinderHeaderValue"
render={({ field }) => ( render={({ field }) => (
<FormItem className="space-y-1"> <FormItem className="space-y-1">
<FormLabel>请求头值</FormLabel> <FormLabel>{t("shellToolConfig.headerValue")}</FormLabel>
<Input {...field} placeholder="Header Value" className="h-8" /> <Input {...field} placeholder={t("shellToolConfig.headerValue")} className="h-8" />
</FormItem> </FormItem>
)} )}
/> />
@@ -360,6 +382,7 @@ function BehinderTabContent({ form, shellTypes }: { form: UseFormReturn<FormSche
} }
function GodzillaTabContent({ form, shellTypes }: { form: UseFormReturn<FormSchema>; shellTypes: Array<string> }) { function GodzillaTabContent({ form, shellTypes }: { form: UseFormReturn<FormSchema>; shellTypes: Array<string> }) {
const { t } = useTranslation();
return ( return (
<FormProvider {...form}> <FormProvider {...form}>
<TabsContent value="Godzilla"> <TabsContent value="Godzilla">
@@ -375,8 +398,8 @@ function GodzillaTabContent({ form, shellTypes }: { form: UseFormReturn<FormSche
name="godzillaPass" name="godzillaPass"
render={({ field }) => ( render={({ field }) => (
<FormItem className="space-y-1"> <FormItem className="space-y-1">
<FormLabel>密码</FormLabel> <FormLabel>{t("shellToolConfig.pass")}</FormLabel>
<Input {...field} placeholder="Pass" className="h-8" /> <Input {...field} placeholder={t("shellToolConfig.pass")} className="h-8" />
</FormItem> </FormItem>
)} )}
/> />
@@ -385,8 +408,8 @@ function GodzillaTabContent({ form, shellTypes }: { form: UseFormReturn<FormSche
name="godzillaKey" name="godzillaKey"
render={({ field }) => ( render={({ field }) => (
<FormItem className="space-y-1"> <FormItem className="space-y-1">
<FormLabel>密钥</FormLabel> <FormLabel>{t("shellToolConfig.key")}</FormLabel>
<Input {...field} placeholder="Key" className="h-8" /> <Input {...field} placeholder={t("shellToolConfig.key")} className="h-8" />
</FormItem> </FormItem>
)} )}
/> />
@@ -395,8 +418,8 @@ function GodzillaTabContent({ form, shellTypes }: { form: UseFormReturn<FormSche
name="godzillaHeaderName" name="godzillaHeaderName"
render={({ field }) => ( render={({ field }) => (
<FormItem className="space-y-1"> <FormItem className="space-y-1">
<FormLabel>请求头键</FormLabel> <FormLabel>{t("shellToolConfig.headerName")}</FormLabel>
<Input {...field} placeholder="Header Name" className="h-8" /> <Input {...field} placeholder={t("shellToolConfig.headerName")} className="h-8" />
</FormItem> </FormItem>
)} )}
/> />
@@ -405,8 +428,8 @@ function GodzillaTabContent({ form, shellTypes }: { form: UseFormReturn<FormSche
name="godzillaHeaderValue" name="godzillaHeaderValue"
render={({ field }) => ( render={({ field }) => (
<FormItem className="space-y-1"> <FormItem className="space-y-1">
<FormLabel>请求头值</FormLabel> <FormLabel>{t("shellToolConfig.headerValue")}</FormLabel>
<Input {...field} placeholder="Header Value" className="h-8" /> <Input {...field} placeholder={t("shellToolConfig.headerValue")} className="h-8" />
</FormItem> </FormItem>
)} )}
/> />
@@ -420,6 +443,7 @@ function GodzillaTabContent({ form, shellTypes }: { form: UseFormReturn<FormSche
} }
function CommandTabContent({ form, shellTypes }: { form: UseFormReturn<FormSchema>; shellTypes: Array<string> }) { function CommandTabContent({ form, shellTypes }: { form: UseFormReturn<FormSchema>; shellTypes: Array<string> }) {
const { t } = useTranslation();
return ( return (
<FormProvider {...form}> <FormProvider {...form}>
<TabsContent value="Command"> <TabsContent value="Command">
@@ -434,11 +458,10 @@ function CommandTabContent({ form, shellTypes }: { form: UseFormReturn<FormSchem
name="commandParamName" name="commandParamName"
render={({ field }) => ( render={({ field }) => (
<FormItem className="space-y-1"> <FormItem className="space-y-1">
<FormLabel>请求参数</FormLabel> <FormLabel>{t("shellToolConfig.paramName")}</FormLabel>
<FormControl> <FormControl>
<Input {...field} placeholder="请输入" className="h-8" /> <Input {...field} placeholder={t("shellToolConfig.paramName")} className="h-8" />
</FormControl> </FormControl>
<FormDescription>填写接收命令的请求参数,例如填 cmd 即 `?cmd=whoami` 来执行命令</FormDescription>
</FormItem> </FormItem>
)} )}
/> />
@@ -449,3 +472,45 @@ function CommandTabContent({ form, shellTypes }: { form: UseFormReturn<FormSchem
</FormProvider> </FormProvider>
); );
} }
function AntSwordTabContent() {
return (
<TabsContent value="AntSword">
<Card>
<CardContent className="space-y-2 mt-4">
<div className="flex items-center justify-center">
<span className="text-gray-500">WIP</span>
</div>
</CardContent>
</Card>
</TabsContent>
);
}
function Suo5TabContent() {
return (
<TabsContent value="Suo5">
<Card>
<CardContent className="space-y-2 mt-4">
<div className="flex items-center justify-center">
<span className="text-gray-500">WIP</span>
</div>
</CardContent>
</Card>
</TabsContent>
);
}
function NeoreGeorgTabContent() {
return (
<TabsContent value="Neo-reGeorg">
<Card>
<CardContent className="space-y-2 mt-4">
<div className="flex items-center justify-center">
<span className="text-gray-500">WIP</span>
</div>
</CardContent>
</Card>
</TabsContent>
);
}
+22 -8
View File
@@ -6,6 +6,12 @@ import { PackerConfig } from "@/types/shell.ts";
import { PackageIcon } from "lucide-react"; import { PackageIcon } from "lucide-react";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { FormProvider, UseFormReturn } from "react-hook-form"; import { FormProvider, UseFormReturn } from "react-hook-form";
import { useTranslation } from "react-i18next";
type Option = {
name: string;
value: string;
};
export function PackageConfigCard({ export function PackageConfigCard({
packerConfig, packerConfig,
@@ -14,12 +20,13 @@ export function PackageConfigCard({
packerConfig: PackerConfig | undefined; packerConfig: PackerConfig | undefined;
form: UseFormReturn<FormSchema>; form: UseFormReturn<FormSchema>;
}) { }) {
const [options, setOptions] = useState<Array<Array<string>>>([]); const [options, setOptions] = useState<Array<Option>>([]);
const shellType = form.watch("shellType"); const shellType = form.watch("shellType");
const { t } = useTranslation();
useEffect(() => { useEffect(() => {
const filteredOptions = Object.entries(packerConfig ?? {}).filter(([name, _]) => { const filteredOptions = (packerConfig ?? []).filter((name) => {
if (!shellType || shellType === " ") { if (!shellType || shellType === " ") {
return true; return true;
} }
@@ -28,18 +35,25 @@ export function PackageConfigCard({
} }
return !name.startsWith("Agent"); return !name.startsWith("Agent");
}); });
setOptions(filteredOptions); setOptions(
filteredOptions.map((name) => {
return {
name: t(`packageConfig.packer.${name}`),
value: name,
};
}),
);
if (filteredOptions.length > 0) { if (filteredOptions.length > 0) {
form.setValue("packingMethod", filteredOptions[0][0]); form.setValue("packingMethod", filteredOptions[0]);
} }
}, [form, packerConfig, shellType]); // Add shellType to the dependency array }, [form, packerConfig, shellType, t]);
return ( return (
<Card className="w-full"> <Card className="w-full">
<CardHeader className="pb-1"> <CardHeader className="pb-1">
<CardTitle className="text-md flex items-center gap-2"> <CardTitle className="text-md flex items-center gap-2">
<PackageIcon className="h-5" /> <PackageIcon className="h-5" />
<span>打包配置</span> <span>{t("configs.package-config")}</span>
</CardTitle> </CardTitle>
</CardHeader> </CardHeader>
<CardContent> <CardContent>
@@ -49,10 +63,10 @@ export function PackageConfigCard({
name="packingMethod" name="packingMethod"
render={({ field }) => ( render={({ field }) => (
<FormItem className="space-y-3"> <FormItem className="space-y-3">
<FormLabel>打包方式</FormLabel> <FormLabel>{t("packageConfig.title")}</FormLabel>
<FormControl> <FormControl>
<RadioGroup onValueChange={field.onChange} value={field.value} className="grid grid-cols-3"> <RadioGroup onValueChange={field.onChange} value={field.value} className="grid grid-cols-3">
{options.map(([name, value]) => ( {options.map(({ name, value }) => (
<FormItem key={value} className="flex items-center space-x-3 space-y-0"> <FormItem key={value} className="flex items-center space-x-3 space-y-0">
<FormControl> <FormControl>
<RadioGroupItem value={value} id={value} /> <RadioGroupItem value={value} id={value} />
+8 -6
View File
@@ -1,18 +1,20 @@
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card.tsx"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card.tsx";
import { useTranslation } from "react-i18next";
export function QuickUsage() { export function QuickUsage() {
const { t } = useTranslation();
return ( return (
<Card> <Card>
<CardHeader> <CardHeader>
<CardTitle>快速使用</CardTitle> <CardTitle>{t("quickUsage.title")}</CardTitle>
</CardHeader> </CardHeader>
<CardContent> <CardContent>
<ol className="list-decimal list-inside space-y-4 text-sm"> <ol className="list-decimal list-inside space-y-4 text-sm">
<li>选择目标服务</li> <li>{t("quickUsage.step1")}</li>
<li>选择内存马功能,Godzilla、Behinder 或者其他</li> <li>{t("quickUsage.step2")}</li>
<li>选择内存马挂载类型,Filter、Listener 或者其他</li> <li>{t("quickUsage.step3")}</li>
<li>选择打包方式</li> <li>{t("quickUsage.step4")}</li>
<li>点击生成内存马</li> <li>{t("quickUsage.step5")}</li>
</ol> </ol>
</CardContent> </CardContent>
</Card> </Card>
+119 -98
View File
@@ -26,6 +26,8 @@ import {
} from "@/types/shell.ts"; } from "@/types/shell.ts";
import { CircleHelpIcon, TriangleAlertIcon } from "lucide-react"; import { CircleHelpIcon, TriangleAlertIcon } from "lucide-react";
import { Fragment } from "react"; import { Fragment } from "react";
import { useTranslation } from "react-i18next";
import { toast } from "sonner";
function AgentResult({ packResult, generateResult }: { packResult: string; generateResult?: GenerateResult }) { function AgentResult({ packResult, generateResult }: { packResult: string; generateResult?: GenerateResult }) {
return ( return (
@@ -77,25 +79,26 @@ function AgentResult({ packResult, generateResult }: { packResult: string; gener
} }
function FeedbackAlert() { function FeedbackAlert() {
const { t } = useTranslation();
return ( return (
<AlertDialog> <AlertDialog>
<AlertDialogTrigger asChild> <AlertDialogTrigger asChild>
<Button variant="outline" type="button"> <Button variant="outline" type="button">
<CircleHelpIcon /> 内存马利用失败 ? <CircleHelpIcon /> {t("shellNotWork.title")}
</Button> </Button>
</AlertDialogTrigger> </AlertDialogTrigger>
<AlertDialogContent> <AlertDialogContent>
<AlertDialogHeader> <AlertDialogHeader>
<AlertDialogTitle>内存马利用失败 ?</AlertDialogTitle> <AlertDialogTitle>{t("shellNotWork.title")}</AlertDialogTitle>
<AlertDialogDescription> <AlertDialogDescription>
<ol> <ol>
<li>1. 尝试开启调试模式,重新生成内存马并注入,查看控制台或日志</li> <li>{t("shellNotWork.step1")}</li>
<li>2. 如果出现异常堆栈信息,或未见异常,请截图当前生成界面以及异常日志,并尽可能描述目标环境进行反馈</li> <li>{t("shellNotWork.step2")}</li>
</ol> </ol>
</AlertDialogDescription> </AlertDialogDescription>
</AlertDialogHeader> </AlertDialogHeader>
<AlertDialogFooter> <AlertDialogFooter>
<AlertDialogCancel>取消</AlertDialogCancel> <AlertDialogCancel>{t("cancel")}</AlertDialogCancel>
<AlertDialogAction <AlertDialogAction
onClick={() => onClick={() =>
window.open( window.open(
@@ -103,7 +106,7 @@ function FeedbackAlert() {
) )
} }
> >
反馈 {t("feedback")}
</AlertDialogAction> </AlertDialogAction>
</AlertDialogFooter> </AlertDialogFooter>
</AlertDialogContent> </AlertDialogContent>
@@ -112,36 +115,40 @@ function FeedbackAlert() {
} }
function BasicInfo({ generateResult }: { generateResult?: GenerateResult }) { function BasicInfo({ generateResult }: { generateResult?: GenerateResult }) {
const { t } = useTranslation();
return ( return (
<Card> <Card>
<CardHeader> <CardHeader>
<CardTitle className="flex items-center justify-between"> <CardTitle className="flex items-center justify-between">
基础信息 {t("generateResult.basicInfo")}
<FeedbackAlert /> <FeedbackAlert />
</CardTitle> </CardTitle>
</CardHeader> </CardHeader>
<CardContent> <CardContent>
<div className="grid grid-cols-2"> <div className="grid grid-cols-2">
<CopyableField label="目标服务" text={generateResult?.shellConfig.server} /> <CopyableField label={t("mainConfig.server")} text={generateResult?.shellConfig.server} />
<CopyableField label="内存马功能" text={generateResult?.shellConfig.shellTool} /> <CopyableField label={t("mainConfig.shellTool")} text={generateResult?.shellConfig.shellTool} />
</div> </div>
<CopyableField label="内存马挂载类型" text={generateResult?.shellConfig.shellType} /> <CopyableField label={t("mainConfig.shellMountType")} text={generateResult?.shellConfig.shellType} />
<CopyableField <CopyableField
label="请求路径" label={t("mainConfig.urlPattern")}
text={generateResult?.injectorConfig.urlPattern} text={generateResult?.injectorConfig.urlPattern}
value={generateResult?.injectorConfig.urlPattern} value={generateResult?.injectorConfig.urlPattern}
/> />
{generateResult?.shellConfig.shellTool === "Behinder" && ( {generateResult?.shellConfig.shellTool === "Behinder" && (
<Fragment> <Fragment>
<CopyableField label="脚本类型" text="jsp" /> <CopyableField label={t("shellToolConfig.behinderScriptType")} text="jsp" />
<CopyableField label="加密类型" text="默认" />
<CopyableField <CopyableField
label="连接密码" label={t("shellToolConfig.behinderEncryptType")}
text={t("shellToolConfig.behinderDefaultEncryptType")}
/>
<CopyableField
label={t("shellToolConfig.behinderPass")}
text={(generateResult?.shellToolConfig as BehinderShellToolConfig).pass} text={(generateResult?.shellToolConfig as BehinderShellToolConfig).pass}
value={(generateResult?.shellToolConfig as BehinderShellToolConfig).pass} value={(generateResult?.shellToolConfig as BehinderShellToolConfig).pass}
/> />
<CopyableField <CopyableField
label="自定义请求头" label={t("shellToolConfig.customHeader")}
text={`${(generateResult?.shellToolConfig as BehinderShellToolConfig).headerName}: ${(generateResult?.shellToolConfig as BehinderShellToolConfig).headerValue}`} text={`${(generateResult?.shellToolConfig as BehinderShellToolConfig).headerName}: ${(generateResult?.shellToolConfig as BehinderShellToolConfig).headerValue}`}
value={`${(generateResult?.shellToolConfig as BehinderShellToolConfig).headerName}: ${(generateResult?.shellToolConfig as BehinderShellToolConfig).headerValue}`} value={`${(generateResult?.shellToolConfig as BehinderShellToolConfig).headerName}: ${(generateResult?.shellToolConfig as BehinderShellToolConfig).headerValue}`}
/> />
@@ -150,19 +157,19 @@ function BasicInfo({ generateResult }: { generateResult?: GenerateResult }) {
{generateResult?.shellConfig.shellTool === "Godzilla" && ( {generateResult?.shellConfig.shellTool === "Godzilla" && (
<Fragment> <Fragment>
<CopyableField <CopyableField
label="密码" label={t("shellToolConfig.pass")}
text={(generateResult?.shellToolConfig as GodzillaShellToolConfig).pass} text={(generateResult?.shellToolConfig as GodzillaShellToolConfig).pass}
value={(generateResult?.shellToolConfig as GodzillaShellToolConfig).pass} value={(generateResult?.shellToolConfig as GodzillaShellToolConfig).pass}
/> />
<CopyableField <CopyableField
label="密钥" label={t("shellToolConfig.key")}
text={(generateResult?.shellToolConfig as GodzillaShellToolConfig).key} text={(generateResult?.shellToolConfig as GodzillaShellToolConfig).key}
value={(generateResult?.shellToolConfig as GodzillaShellToolConfig).key} value={(generateResult?.shellToolConfig as GodzillaShellToolConfig).key}
/> />
<CopyableField label="有效载荷" text="JavaDynamicPayload" /> <CopyableField label={t("shellToolConfig.godzillaPayload")} text="JavaDynamicPayload" />
<CopyableField label="加密器" text="JAVA_AES_BASE64" /> <CopyableField label={t("shellToolConfig.godzillaEncryptor")} text="JAVA_AES_BASE64" />
<CopyableField <CopyableField
label="请求配置 -> 请求头" label={t("shellToolConfig.godzillaHeader")}
text={`${(generateResult?.shellToolConfig as GodzillaShellToolConfig).headerName}: ${(generateResult?.shellToolConfig as GodzillaShellToolConfig).headerValue}`} text={`${(generateResult?.shellToolConfig as GodzillaShellToolConfig).headerName}: ${(generateResult?.shellToolConfig as GodzillaShellToolConfig).headerValue}`}
value={`${(generateResult?.shellToolConfig as GodzillaShellToolConfig).headerName}: ${(generateResult?.shellToolConfig as GodzillaShellToolConfig).headerValue}`} value={`${(generateResult?.shellToolConfig as GodzillaShellToolConfig).headerName}: ${(generateResult?.shellToolConfig as GodzillaShellToolConfig).headerValue}`}
/> />
@@ -171,19 +178,19 @@ function BasicInfo({ generateResult }: { generateResult?: GenerateResult }) {
{generateResult?.shellConfig.shellTool === "Command" && ( {generateResult?.shellConfig.shellTool === "Command" && (
<Fragment> <Fragment>
<CopyableField <CopyableField
label="接收命令请求参数" label={t("shellToolConfig.paramName")}
text={(generateResult?.shellToolConfig as CommandShellToolConfig).paramName} text={(generateResult?.shellToolConfig as CommandShellToolConfig).paramName}
value={(generateResult?.shellToolConfig as CommandShellToolConfig).paramName} value={(generateResult?.shellToolConfig as CommandShellToolConfig).paramName}
/> />
</Fragment> </Fragment>
)} )}
<CopyableField <CopyableField
label="注入器类名" label={t("mainConfig.injectorClassName")}
value={generateResult?.injectorClassName} value={generateResult?.injectorClassName}
text={`${generateResult?.injectorClassName} (${generateResult?.injectorSize} bytes)`} text={`${generateResult?.injectorClassName} (${generateResult?.injectorSize} bytes)`}
/> />
<CopyableField <CopyableField
label="内存马类名" label={t("mainConfig.shellClassName")}
value={generateResult?.shellClassName} value={generateResult?.shellClassName}
text={`${generateResult?.shellClassName} (${generateResult?.shellSize} bytes)`} text={`${generateResult?.shellClassName} (${generateResult?.shellSize} bytes)`}
/> />
@@ -199,81 +206,95 @@ export function ShellResult({
}: { packResult: string; packMethod: string; generateResult?: GenerateResult }) { }: { packResult: string; packMethod: string; generateResult?: GenerateResult }) {
const showCode = packMethod === "JSP"; const showCode = packMethod === "JSP";
const isAgent = packMethod.startsWith("Agent"); const isAgent = packMethod.startsWith("Agent");
const { t } = useTranslation();
return ( return (
<Tabs defaultValue="packResult"> <Fragment>
<TabsList className="grid w-full grid-cols-3"> {generateResult ? (
<TabsTrigger value="packResult">打包结果</TabsTrigger> <Tabs defaultValue="packResult">
<TabsTrigger value="shell">内存马类</TabsTrigger> <TabsList className="grid w-full grid-cols-3">
<TabsTrigger value="injector">注入器类</TabsTrigger> <TabsTrigger value="packResult">{t("generateResult.title1")}</TabsTrigger>
</TabsList> <TabsTrigger value="shell">{t("generateResult.title2")}</TabsTrigger>
<TabsContent value="packResult" className="my-4"> <TabsTrigger value="injector">{t("generateResult.title3")}</TabsTrigger>
<div className="mb-4"> </TabsList>
{generateResult && <BasicInfo generateResult={generateResult} />} <TabsContent value="packResult" className="my-4">
{!generateResult && <QuickUsage />} <div className="mb-4">
</div> <BasicInfo generateResult={generateResult} />
{!isAgent && ( </div>
<CodeViewer {!isAgent && (
code={packResult} <CodeViewer
wrapLongLines={!showCode} code={packResult}
showLineNumbers={showCode} wrapLongLines={!showCode}
language={showCode ? "java" : "text"} showLineNumbers={showCode}
height={400} language={showCode ? "java" : "text"}
/> height={400}
)} />
{isAgent && <AgentResult packResult={packResult} generateResult={generateResult} />} )}
</TabsContent> {isAgent && <AgentResult packResult={packResult} generateResult={generateResult} />}
<TabsContent value="shell" className="mt-4"> </TabsContent>
<Alert> <TabsContent value="shell" className="mt-4">
<TriangleAlertIcon className="h-4 w-4" /> <Alert>
<AlertTitle>Warning</AlertTitle> <TriangleAlertIcon className="h-4 w-4" />
<AlertDescription>反编译还在开发中,因此当前仅能看到 base64 编码格式</AlertDescription> <AlertTitle>Warning</AlertTitle>
</Alert> <AlertDescription>{t("tips.decompileTip")}</AlertDescription>
<div className="gap-4 my-2 flex items-center justify-end"> </Alert>
{generateResult && ( <div className="gap-4 my-2 flex items-center justify-end">
<Button <Button
size="sm" size="sm"
variant="outline" variant="outline"
className="w-28" className="w-28"
type="button" type="button"
onClick={() => downloadBytes(generateResult?.shellBytesBase64Str, generateResult?.shellClassName)} onClick={() => {
> if (!generateResult?.shellBytesBase64Str) {
下载 Class toast.warning(t("tips.shellBytesEmpty"));
</Button> return;
)} }
</div> downloadBytes(generateResult?.shellBytesBase64Str, generateResult?.shellClassName);
<CodeViewer }}
showLineNumbers={false} >
wrapLongLines={true} {t("download")} Class
code={generateResult?.shellBytesBase64Str ?? ""} </Button>
language="text" </div>
/> <CodeViewer
</TabsContent> showLineNumbers={false}
<TabsContent value="injector" className="mt-4"> wrapLongLines={true}
<Alert> code={generateResult?.shellBytesBase64Str ?? ""}
<TriangleAlertIcon className="h-4 w-4" /> language="text"
<AlertTitle>Warning</AlertTitle> />
<AlertDescription>反编译还在开发中,因此当前仅能看到 base64 编码格式</AlertDescription> </TabsContent>
</Alert> <TabsContent value="injector" className="mt-4">
<div className="gap-4 my-2 flex items-center justify-end"> <Alert>
{generateResult && ( <TriangleAlertIcon className="h-4 w-4" />
<Button <AlertTitle>Warning</AlertTitle>
size="sm" <AlertDescription>{t("tips.decompileTip")}</AlertDescription>
className="w-28" </Alert>
variant="outline" <div className="gap-4 my-2 flex items-center justify-end">
type="button" <Button
onClick={() => downloadBytes(generateResult?.injectorBytesBase64Str, generateResult?.injectorClassName)} size="sm"
> className="w-28"
下载 Class variant="outline"
</Button> type="button"
)} onClick={() => {
</div> if (!generateResult?.injectorBytesBase64Str) {
<CodeViewer toast.warning(t("tips.shellBytesEmpty"));
showLineNumbers={false} return;
wrapLongLines={true} }
code={generateResult?.injectorBytesBase64Str ?? ""} downloadBytes(generateResult?.injectorBytesBase64Str, generateResult?.injectorClassName);
language="text" }}
/> >
</TabsContent> {t("download")} Class
</Tabs> </Button>
</div>
<CodeViewer
showLineNumbers={false}
wrapLongLines={true}
code={generateResult?.injectorBytesBase64Str ?? ""}
language="text"
/>
</TabsContent>
</Tabs>
) : (
<QuickUsage />
)}
</Fragment>
); );
} }
+4 -2
View File
@@ -1,7 +1,9 @@
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip.tsx"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip.tsx";
import { InfoIcon } from "lucide-react"; import { InfoIcon } from "lucide-react";
import { useTranslation } from "react-i18next";
export function JreTip() { export function JreTip() {
const { t } = useTranslation();
return ( return (
<TooltipProvider> <TooltipProvider>
<Tooltip> <Tooltip>
@@ -9,8 +11,8 @@ export function JreTip() {
<InfoIcon className="cursor-pointer h-4" /> <InfoIcon className="cursor-pointer h-4" />
</TooltipTrigger> </TooltipTrigger>
<TooltipContent> <TooltipContent>
<p>目标 JRE 版本,一般而言为了最大的兼容性,默认 Java 6 即可,Java 高版本能加载低版本的字节码。</p> <p>{t("tips.jreTip")}</p>
<p>特定情况下,例如 JDK8 才能使用 lambda 表达式,JDK9 以上存在模块限制时才需要选择特定的版本。</p> <p>{t("tips.jreTip2")}</p>
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider> </TooltipProvider>
+5 -4
View File
@@ -1,7 +1,9 @@
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip.tsx"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip.tsx";
import { InfoIcon } from "lucide-react"; import { InfoIcon } from "lucide-react";
import { useTranslation } from "react-i18next";
export function UrlPatternTip() { export function UrlPatternTip() {
const { t } = useTranslation();
return ( return (
<TooltipProvider> <TooltipProvider>
<Tooltip> <Tooltip>
@@ -9,10 +11,9 @@ export function UrlPatternTip() {
<InfoIcon className="cursor-pointer h-4" /> <InfoIcon className="cursor-pointer h-4" />
</TooltipTrigger> </TooltipTrigger>
<TooltipContent> <TooltipContent>
<p>当使用 Servlet 内存马时必须写具体的 urlPattern,不能使用 /*,不然无法使用</p> <p>{t("tips.servletUrlPattern")}</p>
<p>当使用 SpringMVC ControllerHandler 内存马时必须写具体的 urlPattern,不能使用 /*,不然无法使用</p> <p>{t("tips.controllerUrlPattern")}</p>
<p>当使用 SpringWebFlux HandlerMethod 内存马时必须写具体的 urlPattern,不能使用 /*,不然无法使用</p> <p>{t("tips.handlerUrlPattern")}</p>
<p>当使用 SpringWebFlux HandlerFunction 内存马时必须写具体的 urlPattern,不能使用 /*,不然无法使用</p>
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider> </TooltipProvider>
+32
View File
@@ -0,0 +1,32 @@
import i18n from "i18next";
import { initReactI18next } from "react-i18next";
import { resources } from "./translations";
const getStoredLanguage = () => {
const storedLang = localStorage.getItem("i18nextLng");
if (storedLang && ["en", "zh"].includes(storedLang)) {
return storedLang;
}
const browserLang = navigator.language.split("-")[0];
return ["en", "zh"].includes(browserLang) ? browserLang : "en";
};
i18n.use(initReactI18next).init({
resources,
lng: getStoredLanguage(),
fallbackLng: "en",
interpolation: {
escapeValue: false,
},
detection: {
order: ["localStorage", "navigator"],
},
saveMissing: true,
load: "languageOnly",
});
i18n.on("languageChanged", (lng) => {
localStorage.setItem("i18nextLng", lng);
});
export default i18n;
+217
View File
@@ -0,0 +1,217 @@
export const resources = {
en: {
translation: {
download: "Downlaod",
feedback: "Feedback",
cancel: "Cancel",
placeholders: {
select: "Please select",
input: "Please input",
},
configs: {
"main-config": "Main Config",
"package-config": "Package Config",
},
optional: "(Optional)",
mainConfig: {
server: "Target Server",
jre: "Target JRE Version",
debug: "Debug Mode",
bypassJavaModule: "Bypass Java Module",
shellMountType: "Shell Mount Type",
shellTool: "Shell Tool",
urlPattern: "URL Pattern",
shellClassName: "Shell ClassName",
injectorClassName: "Injector ClassName",
},
shellToolConfig: {
godzilla: "Godzilla",
behinder: "Behinder",
command: "Command",
customHeader: "Custom Header",
headerName: "Header Name",
headerValue: "Header Value",
pass: "Pass",
key: "Key",
godzillaHeader: "Request Config -> Request Header",
godzillaPayload: "Payload",
godzillaEncryptor: "Encryptor",
behinderPass: "Pass",
behinderScriptType: "Script Type",
behinderEncryptType: "Encrypt Type",
behinderDefaultEncryptType: "Default",
paramName: "Param Name",
},
packageConfig: {
title: "Package Method",
packer: {
Base64: "Base64",
BCEL: "BCEL",
JSP: "JSP",
JAR: "JAR",
EL: "EL",
SpEL: "SpEL",
OGNL: "OGNL",
MVEL: "MVEL",
Freemarker: "Freemarker",
Velocity: "Velocity",
Groovy: "Groovy",
AgentJar: "AgentJar",
Deserialize: "Deserialize(Only CB4, 1.9.x)",
ScriptEngine: "ScriptEngine",
},
},
tips: {
shellToolNotSelected: "Please select a shell tool type first",
targetServerNotFound: "Target server not found?",
waitingForGeneration: "// Waiting for generation...",
targetServerRequest: "Request",
servletUrlPattern: "Servlet type requires a specific URL Pattern, e.g., /hello_servlet",
controllerUrlPattern: "ControllerHandler type requires a specific URL Pattern, e.g., /hello_controller",
handlerUrlPattern: "HandlerMethod/HandlerFunction type requires a specific URL Pattern, e.g., /hello_handler",
jreTip:
"Target JRE version, generally speaking, Java 6 is the default version for maximum compatibility, and Java high versions can load low version bytecode.",
jreTip2:
"In specific cases, such as JDK8 being able to use lambda expressions, and JDK9 and above having module restrictions, a specific version is required.",
decompileTip: "Decompilation is still under development, so the current only sees the base64 encoding format",
shellBytesEmpty: "Shell bytes is empty, please generate shell first",
},
quickUsage: {
title: "Quick Usage",
step1: "Select Target Server",
step2: "Select Shell Tool, Godzilla, Behinder, etc.",
step3: "Select Shell Mount Type, Filter, Listener, etc.",
step4: "Select Packing Method",
step5: "Click Generate Shell",
},
generateResult: {
basicInfo: "Basic Info",
usage: "Usage",
title1: "Generate Result",
title2: "Shell Class",
title3: "Injector Class",
},
shellNotWork: {
title: "Shell Not Work ?",
step1: "1. Try to enable the debug mode, regenerate the memory shell and inject it, check the console or log",
step2:
"2. If an exception stack trace is displayed, or no exception is seen, please take a screenshot of the current generation interface and the exception log, and describe the target environment as much as possible for feedback",
},
buttons: {
generate: "Generate Shell",
},
success: {
generated: "Generation successful",
},
errors: {
generationFailed: "Generation failed, {{error}}",
},
},
},
zh: {
translation: {
download: "下载",
feedback: "反馈",
cancel: "取消",
placeholders: {
select: "请选择",
input: "请输入",
},
configs: {
"main-config": "核心配置",
"package-config": "打包配置",
},
optional: "(可选)",
mainConfig: {
server: "目标服务",
jre: "目标 JRE 版本",
debug: "调试模式",
bypassJavaModule: "绕过 Java 模块限制",
shellMountType: "内存马挂载类型",
shellTool: "内存马功能",
urlPattern: "请求路径",
shellClassName: "内存马类名",
injectorClassName: "注入器类名",
},
shellToolConfig: {
godzilla: "哥斯拉",
behinder: "冰蝎",
command: "命令回显",
customHeader: "自定义请求头",
headerName: "请求头键",
headerValue: "请求头值",
pass: "密码",
key: "密钥",
godzillaPayload: "有效载荷",
godzillaEncryptor: "加密器",
godzillaHeader: "请求配置 -> 请求头",
behinderPass: "连接密码",
behinderScriptType: "脚本类型",
behinderEncryptType: "加密类型",
behinderDefaultEncryptType: "默认",
paramName: "请求参数",
},
packageConfig: {
title: "打包方式",
packer: {
Base64: "Base64",
BCEL: "BCEL",
JSP: "JSP",
JAR: "JAR",
EL: "EL 表达式",
SpEL: "SpEL 表达式",
OGNL: "OGNL 表达式",
MVEL: "MVEL 表达式",
Freemarker: "Freemarker",
Velocity: "Velocity",
Groovy: "Groovy",
AgentJar: "AgentJar",
Deserialize: "反序列化(仅支持 CB4, 1.9.x)",
ScriptEngine: "脚本引擎",
},
},
tips: {
shellToolNotSelected: "请先选择内存马工具类型",
targetServerNotFound: "找不到目标服务 ?",
targetServerRequest: "请求适配",
waitingForGeneration: "// 等待填写参数生成中...",
servletUrlPattern: "Servlet 类型的需要填写具体的 URL Pattern,例如 /hello_servlet",
controllerUrlPattern: "ControllerHandler 类型的需要填写具体的 URL Pattern,例如 /hello_controller",
handlerUrlPattern: "HandlerMethod/HandlerFunction 类型的需要填写具体的 URL Pattern,例如 /hello_handler",
jreTip: "目标 JRE 版本,一般而言为了最大的兼容性,默认 Java 6 即可,Java 高版本能加载低版本的字节码。",
jreTip2: "特定情况下,例如 JDK8 才能使用 lambda 表达式,JDK9 以上存在模块限制时才需要选择特定的版本。",
decompileTip: "反编译还在开发中,因此当前仅能看到 base64 编码格式",
shellBytesEmpty: "内存马字节码为空,无法下载, 请先生成内存马",
},
quickUsage: {
title: "快速使用",
step1: "选择目标服务",
step2: "选择内存马功能,Godzilla、Behinder 或者其他",
step3: "选择内存马挂载类型,Filter、Listener 或者其他",
step4: "选择打包方式",
step5: "点击生成内存马",
},
generateResult: {
basicInfo: "基本信息",
usage: "使用方法",
title1: "生成结果",
title2: "内存马类",
title3: "注入器类",
},
shellNotWork: {
title: "内存马利用失败 ?",
step1: "1. 尝试开启调试模式,重新生成内存马并注入,查看控制台或日志",
step2: "2. 如果出现异常堆栈信息,或未见异常,请截图当前生成界面以及异常日志,并尽可能描述目标环境进行反馈",
},
buttons: {
generate: "生成内存马",
},
success: {
generated: "生成成功",
},
errors: {
generationFailed: "生成失败,{{error}}",
},
},
},
};
+1 -6
View File
@@ -1,16 +1,11 @@
import { type ClassValue, clsx } from "clsx"; import { type ClassValue, clsx } from "clsx";
import { toast } from "sonner";
import { twMerge } from "tailwind-merge"; import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) { export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs)); return twMerge(clsx(inputs));
} }
export function downloadBytes(base64String?: string, className?: string, jarName?: string) { export function downloadBytes(base64String: string, className?: string, jarName?: string) {
if (!base64String) {
toast.warning("字节码为空,无法下载, 请先生成内存马");
return;
}
const byteCharacters = atob(base64String); const byteCharacters = atob(base64String);
const byteNumbers = new Array(byteCharacters.length); const byteNumbers = new Array(byteCharacters.length);
for (let i = 0; i < byteCharacters.length; i++) { for (let i = 0; i < byteCharacters.length; i++) {
+7 -3
View File
@@ -6,6 +6,8 @@ import { TailwindIndicator } from "@/components/tailwind-indicator.tsx";
import { Toaster } from "@/components/ui/sonner.tsx"; import { Toaster } from "@/components/ui/sonner.tsx";
import { env } from "@/config.ts"; import { env } from "@/config.ts";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { I18nextProvider } from "react-i18next";
import i18n from "./i18n/i18n";
const queryClient = new QueryClient(); const queryClient = new QueryClient();
@@ -26,9 +28,11 @@ if (!rootElement.innerHTML) {
const root = ReactDOM.createRoot(rootElement); const root = ReactDOM.createRoot(rootElement);
root.render( root.render(
<QueryClientProvider client={queryClient}> <QueryClientProvider client={queryClient}>
<Toaster /> <I18nextProvider i18n={i18n}>
<RouterProvider router={router} /> <Toaster />
{env.MODE !== "production" && <TailwindIndicator />} <RouterProvider router={router} />
{env.MODE !== "production" && <TailwindIndicator />}
</I18nextProvider>
</QueryClientProvider>, </QueryClientProvider>,
); );
} }
+2
View File
@@ -1,3 +1,4 @@
import { LanguageSwitcher } from "@/components/language-switcher";
import { ModeToggle } from "@/components/mode-toggle.tsx"; import { ModeToggle } from "@/components/mode-toggle.tsx";
import { ThemeProvider } from "@/components/theme-provider.tsx"; import { ThemeProvider } from "@/components/theme-provider.tsx";
import { Button } from "@/components/ui/button.tsx"; import { Button } from "@/components/ui/button.tsx";
@@ -17,6 +18,7 @@ function RootComponent() {
<h2 className="text-lg font-semibold">MemShellParty - JavaWeb</h2> <h2 className="text-lg font-semibold">MemShellParty - JavaWeb</h2>
</div> </div>
<div className="flex gap-1 mr-4"> <div className="flex gap-1 mr-4">
<LanguageSwitcher />
<Button <Button
variant="ghost" variant="ghost"
size="icon" size="icon"
+11 -20
View File
@@ -13,6 +13,7 @@ import { createFileRoute } from "@tanstack/react-router";
import { LoaderCircle, WandSparklesIcon } from "lucide-react"; import { LoaderCircle, WandSparklesIcon } from "lucide-react";
import { useState, useTransition } from "react"; import { useState, useTransition } from "react";
import { useForm } from "react-hook-form"; import { useForm } from "react-hook-form";
import { useTranslation } from "react-i18next";
import { toast } from "sonner"; import { toast } from "sonner";
export const Route = createFileRoute("/")({ export const Route = createFileRoute("/")({
@@ -27,6 +28,7 @@ function IndexComponent() {
return await response.json(); return await response.json();
}, },
}); });
const { t } = useTranslation();
const form = useForm<FormSchema>({ const form = useForm<FormSchema>({
resolver: zodResolver(formSchema), resolver: zodResolver(formSchema),
defaultValues: { defaultValues: {
@@ -51,19 +53,19 @@ function IndexComponent() {
}, },
}); });
const [packResult, setPackResult] = useState<string>("// 等待填写参数生成中"); const [packResult, setPackResult] = useState<string>();
const [generateResult, setGenerateResult] = useState<GenerateResult>(); const [generateResult, setGenerateResult] = useState<GenerateResult>();
const [packMethod, setPackMethod] = useState<string>(""); const [packMethod, setPackMethod] = useState<string>("");
const [isActionPending, startTransition] = useTransition(); const [isActionPending, startTransition] = useTransition();
function customValidation(values: FormSchema) { function customValidation(values: FormSchema) {
if (values.shellType.endsWith("Servlet") && (values.urlPattern === "/*" || !values.urlPattern)) { if (values.shellType.endsWith("Servlet") && (values.urlPattern === "/*" || !values.urlPattern)) {
toast.warning("Servlet 类型的需要填写具体的 URL Pattern,例如 /hello_servlet"); toast.warning(t("tips.servletUrlPattern"));
return false; return false;
} }
if (values.shellType.endsWith("ControllerHandler") && (values.urlPattern === "/*" || !values.urlPattern)) { if (values.shellType.endsWith("ControllerHandler") && (values.urlPattern === "/*" || !values.urlPattern)) {
toast.warning("ControllerHandler 类型的需要填写具体的 URL Pattern,例如 /hello_controller"); toast.warning(t("tips.controllerUrlPattern"));
return false; return false;
} }
@@ -71,20 +73,9 @@ function IndexComponent() {
(values.shellType === "HandlerMethod" || values.shellType === "HandlerFunction") && (values.shellType === "HandlerMethod" || values.shellType === "HandlerFunction") &&
(values.urlPattern === "/*" || !values.urlPattern) (values.urlPattern === "/*" || !values.urlPattern)
) { ) {
toast.warning("HandlerMethod/HandlerFunction 类型的需要填写具体的 URL Pattern,例如 /hello_handler"); toast.warning(t("tips.handlerUrlPattern"));
return false; return false;
} }
if (values.shellType.startsWith("Agent") && values.packingMethod !== "AgentJar") {
toast.warning("Agent 注入方式当前仅支持 AgentJar 打包方式");
return false;
}
if (!values.shellType.startsWith("Agent") && values.packingMethod === "AgentJar") {
toast.warning("Agent 注入方式当前仅支持 Tomcat,只有 Agent 注入方式才可使用 AgentJar 打包方式");
return false;
}
return true; return true;
} }
@@ -103,20 +94,20 @@ function IndexComponent() {
}, },
body: JSON.stringify(postData), body: JSON.stringify(postData),
}); });
await new Promise((resolve) => setTimeout(resolve, 500)); await new Promise((resolve) => setTimeout(resolve, 200));
if (response.ok) { if (response.ok) {
const json: GenerateResponse = await response.json(); const json: GenerateResponse = await response.json();
setPackResult(json.packResult); setPackResult(json.packResult);
setGenerateResult(json.generateResult); setGenerateResult(json.generateResult);
setPackMethod(values.packingMethod); setPackMethod(values.packingMethod);
toast.success("生成成功"); toast.success(t("success.generated"));
} else { } else {
const json: APIErrorResponse = await response.json(); const json: APIErrorResponse = await response.json();
toast.error(`生成失败,${json.error}`); toast.error(t("errors.generationFailed", { error: json.error }));
} }
} catch (err) { } catch (err) {
const error = err as Error; const error = err as Error;
toast.error(`生成失败,${error.message}`); toast.error(t("errors.generationFailed", { error: error.message }));
} }
}); });
} }
@@ -129,7 +120,7 @@ function IndexComponent() {
<PackageConfigCard packerConfig={data?.packers} form={form} /> <PackageConfigCard packerConfig={data?.packers} form={form} />
<Button className="w-full" type="submit" disabled={isActionPending}> <Button className="w-full" type="submit" disabled={isActionPending}>
{isActionPending ? <LoaderCircle className="animate-spin" /> : <WandSparklesIcon />} {isActionPending ? <LoaderCircle className="animate-spin" /> : <WandSparklesIcon />}
生成内存马 {t("buttons.generate")}
</Button> </Button>
</div> </div>
<div className="w-full xl:w-1/2 space-y-4"> <div className="w-full xl:w-1/2 space-y-4">
+1 -3
View File
@@ -58,9 +58,7 @@ export interface MainConfig {
}; };
} }
export interface PackerConfig { export type PackerConfig = Array<string>;
[packerName: string]: string;
}
export interface GenerateResponse { export interface GenerateResponse {
packResult: string; packResult: string;