diff --git a/boot/src/main/java/com/reajason/javaweb/boot/controller/ConfigController.java b/boot/src/main/java/com/reajason/javaweb/boot/controller/ConfigController.java index 14390270..c0fb508d 100644 --- a/boot/src/main/java/com/reajason/javaweb/boot/controller/ConfigController.java +++ b/boot/src/main/java/com/reajason/javaweb/boot/controller/ConfigController.java @@ -10,7 +10,10 @@ import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.RequestMapping; 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; /** @@ -48,13 +51,7 @@ public class ConfigController { .collect(Collectors.toList()) ); config.setCore(coreMap); - config.setPackers(Arrays.stream(Packer.INSTANCE.values()) - .collect(Collectors.toMap( - Packer.INSTANCE::getDesc, - Packer.INSTANCE::name, - (e1, e2) -> e1, - LinkedHashMap::new - ))); + config.setPackers(Arrays.stream(Packer.INSTANCE.values()).map(Packer.INSTANCE::name).toList()); return ResponseEntity.ok(config); } } \ No newline at end of file diff --git a/boot/src/main/java/com/reajason/javaweb/boot/entity/Config.java b/boot/src/main/java/com/reajason/javaweb/boot/entity/Config.java index 959dd951..6f91466a 100644 --- a/boot/src/main/java/com/reajason/javaweb/boot/entity/Config.java +++ b/boot/src/main/java/com/reajason/javaweb/boot/entity/Config.java @@ -13,5 +13,5 @@ import java.util.Map; public class Config { private List servers; private Map> core; - private Map packers; + private List packers; } \ No newline at end of file diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/packer/Packer.java b/generator/src/main/java/com/reajason/javaweb/memshell/packer/Packer.java index f8751b76..7a70c29e 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/packer/Packer.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/packer/Packer.java @@ -42,49 +42,46 @@ public interface Packer { /** * Base64 */ - Base64("Base64", new Base64Packer()), + Base64(new Base64Packer()), /** * BCEL */ - BCEL("BCEL", new BCELPacker()), + BCEL(new BCELPacker()), /** * 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 表达式", 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; - INSTANCE(String desc, Packer packer) { - this.desc = desc; + INSTANCE(Packer packer) { this.packer = packer; } } diff --git a/web/bun.lockb b/web/bun.lockb index c7910757..79dccd11 100755 Binary files a/web/bun.lockb and b/web/bun.lockb differ diff --git a/web/package.json b/web/package.json index 197cdc7a..db82c4ad 100644 --- a/web/package.json +++ b/web/package.json @@ -13,7 +13,7 @@ }, "devDependencies": { "@biomejs/biome": "1.9.4", - "@tanstack/router-plugin": "^1.97.1", + "@tanstack/router-plugin": "^1.97.3", "@types/node": "^22.10.7", "@types/react": "^19.0.7", "@types/react-dom": "^19.0.3", @@ -39,17 +39,19 @@ "@radix-ui/react-switch": "^1.1.2", "@radix-ui/react-tabs": "^1.1.2", "@radix-ui/react-tooltip": "^1.1.6", - "@tanstack/react-query": "^5.64.1", - "@tanstack/react-router": "^1.97.1", - "@tanstack/router-devtools": "^1.97.1", + "@tanstack/react-query": "^5.64.2", + "@tanstack/react-router": "^1.97.3", + "@tanstack/router-devtools": "^1.97.3", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", + "i18next": "^24.2.1", "lucide-react": "^0.473.0", "next-themes": "^0.4.4", "react": "^19.0.0", "react-copy-to-clipboard": "^5.1.0", "react-dom": "^19.0.0", "react-hook-form": "^7.54.2", + "react-i18next": "^15.4.0", "react-syntax-highlighter": "^15.6.1", "sonner": "^1.7.2", "tailwind-merge": "^2.6.0", diff --git a/web/src/components/language-switcher.tsx b/web/src/components/language-switcher.tsx new file mode 100644 index 00000000..69829e0a --- /dev/null +++ b/web/src/components/language-switcher.tsx @@ -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 ( + + ); +} diff --git a/web/src/components/main-config-card.tsx b/web/src/components/main-config-card.tsx index 59ef91ed..fe691e0a 100644 --- a/web/src/components/main-config-card.tsx +++ b/web/src/components/main-config-card.tsx @@ -14,6 +14,7 @@ import { cn } from "@/lib/utils.ts"; import { ArrowUpRightIcon, ServerIcon } from "lucide-react"; import { useState } from "react"; import { FormProvider, UseFormReturn } from "react-hook-form"; +import { useTranslation } from "react-i18next"; const JDKVersion = [ { name: "Java6", value: "50" }, @@ -34,9 +35,17 @@ export function MainConfigCard({ servers?: string[]; }) { const [shellToolMap, setShellToolMap] = useState<{ [toolName: string]: string[] }>(); - const [shellTools, setShellTools] = useState(["Behinder", "Godzilla", "Command"]); + const [shellTools, setShellTools] = useState([ + "Behinder", + "Godzilla", + "Command", + "AntSword", + "Suo5", + "Neo-reGeorg", + ]); const [shellTypes, setShellTypes] = useState([]); const shellTool = form.watch("shellTool"); + const { t } = useTranslation(); const handleServerChange = (value: string) => { if (mainConfig) { @@ -97,7 +106,7 @@ export function MainConfigCard({ - 生成配置 + {t("configs.main-config")} @@ -107,7 +116,7 @@ export function MainConfigCard({ name="server" render={({ field }) => ( - 目标服务 + {t("mainConfig.server")} - 下拉列表找不到目标服务 ? + {t("tips.targetServerNotFound")}  - 请求适配 + {t("tips.targetServerRequest")} @@ -149,7 +158,7 @@ export function MainConfigCard({ render={({ field }) => ( - + @@ -252,7 +267,7 @@ function ShellTypeFormField({ form, shellTypes }: { form: UseFormReturn )) ) : ( - 请先选择内存马工具类型 + {t("tips.shellToolNotSelected")} )} @@ -264,6 +279,7 @@ function ShellTypeFormField({ form, shellTypes }: { form: UseFormReturn }) { + const { t } = useTranslation(); return ( }) { render={({ field }) => ( - + )} /> @@ -283,6 +299,7 @@ function UrlPatternFormField({ form }: { form: UseFormReturn }) { } function OptionalClassFormField({ form }: { form: UseFormReturn }) { + const { t } = useTranslation(); return ( }) { name="shellClassName" render={({ field }) => ( - 内存马类名(可选) - + + {t("mainConfig.shellClassName")} {t("optional")} + + )} /> @@ -300,8 +319,10 @@ function OptionalClassFormField({ form }: { form: UseFormReturn }) { name="injectorClassName" render={({ field }) => ( - 注入器类名(可选) - + + {t("mainConfig.injectorClassName")} {t("optional")} + + )} /> @@ -310,6 +331,7 @@ function OptionalClassFormField({ form }: { form: UseFormReturn }) { } function BehinderTabContent({ form, shellTypes }: { form: UseFormReturn; shellTypes: Array }) { + const { t } = useTranslation(); return ( @@ -324,8 +346,8 @@ function BehinderTabContent({ form, shellTypes }: { form: UseFormReturn ( - 连接密码 - + {t("shellToolConfig.behinderPass")} + )} /> @@ -335,8 +357,8 @@ function BehinderTabContent({ form, shellTypes }: { form: UseFormReturn ( - 请求头键 - + {t("shellToolConfig.headerName")} + )} /> @@ -345,8 +367,8 @@ function BehinderTabContent({ form, shellTypes }: { form: UseFormReturn ( - 请求头值 - + {t("shellToolConfig.headerValue")} + )} /> @@ -360,6 +382,7 @@ function BehinderTabContent({ form, shellTypes }: { form: UseFormReturn; shellTypes: Array }) { + const { t } = useTranslation(); return ( @@ -375,8 +398,8 @@ function GodzillaTabContent({ form, shellTypes }: { form: UseFormReturn ( - 密码 - + {t("shellToolConfig.pass")} + )} /> @@ -385,8 +408,8 @@ function GodzillaTabContent({ form, shellTypes }: { form: UseFormReturn ( - 密钥 - + {t("shellToolConfig.key")} + )} /> @@ -395,8 +418,8 @@ function GodzillaTabContent({ form, shellTypes }: { form: UseFormReturn ( - 请求头键 - + {t("shellToolConfig.headerName")} + )} /> @@ -405,8 +428,8 @@ function GodzillaTabContent({ form, shellTypes }: { form: UseFormReturn ( - 请求头值 - + {t("shellToolConfig.headerValue")} + )} /> @@ -420,6 +443,7 @@ function GodzillaTabContent({ form, shellTypes }: { form: UseFormReturn; shellTypes: Array }) { + const { t } = useTranslation(); return ( @@ -434,11 +458,10 @@ function CommandTabContent({ form, shellTypes }: { form: UseFormReturn ( - 请求参数 + {t("shellToolConfig.paramName")} - + - 填写接收命令的请求参数,例如填 cmd 即 `?cmd=whoami` 来执行命令 )} /> @@ -449,3 +472,45 @@ function CommandTabContent({ form, shellTypes }: { form: UseFormReturn ); } + +function AntSwordTabContent() { + return ( + + + +
+ WIP +
+
+
+
+ ); +} + +function Suo5TabContent() { + return ( + + + +
+ WIP +
+
+
+
+ ); +} + +function NeoreGeorgTabContent() { + return ( + + + +
+ WIP +
+
+
+
+ ); +} diff --git a/web/src/components/package-config-card.tsx b/web/src/components/package-config-card.tsx index a1afdd80..9358f370 100644 --- a/web/src/components/package-config-card.tsx +++ b/web/src/components/package-config-card.tsx @@ -6,6 +6,12 @@ import { PackerConfig } from "@/types/shell.ts"; import { PackageIcon } from "lucide-react"; import { useEffect, useState } from "react"; import { FormProvider, UseFormReturn } from "react-hook-form"; +import { useTranslation } from "react-i18next"; + +type Option = { + name: string; + value: string; +}; export function PackageConfigCard({ packerConfig, @@ -14,12 +20,13 @@ export function PackageConfigCard({ packerConfig: PackerConfig | undefined; form: UseFormReturn; }) { - const [options, setOptions] = useState>>([]); + const [options, setOptions] = useState>([]); const shellType = form.watch("shellType"); + const { t } = useTranslation(); useEffect(() => { - const filteredOptions = Object.entries(packerConfig ?? {}).filter(([name, _]) => { + const filteredOptions = (packerConfig ?? []).filter((name) => { if (!shellType || shellType === " ") { return true; } @@ -28,18 +35,25 @@ export function PackageConfigCard({ } return !name.startsWith("Agent"); }); - setOptions(filteredOptions); + setOptions( + filteredOptions.map((name) => { + return { + name: t(`packageConfig.packer.${name}`), + value: name, + }; + }), + ); 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 ( - 打包配置 + {t("configs.package-config")} @@ -49,10 +63,10 @@ export function PackageConfigCard({ name="packingMethod" render={({ field }) => ( - 打包方式 + {t("packageConfig.title")} - {options.map(([name, value]) => ( + {options.map(({ name, value }) => ( diff --git a/web/src/components/quick-usage.tsx b/web/src/components/quick-usage.tsx index 8cf06ca9..57691182 100644 --- a/web/src/components/quick-usage.tsx +++ b/web/src/components/quick-usage.tsx @@ -1,18 +1,20 @@ import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card.tsx"; +import { useTranslation } from "react-i18next"; export function QuickUsage() { + const { t } = useTranslation(); return ( - 快速使用 + {t("quickUsage.title")}
    -
  1. 选择目标服务
  2. -
  3. 选择内存马功能,Godzilla、Behinder 或者其他
  4. -
  5. 选择内存马挂载类型,Filter、Listener 或者其他
  6. -
  7. 选择打包方式
  8. -
  9. 点击生成内存马
  10. +
  11. {t("quickUsage.step1")}
  12. +
  13. {t("quickUsage.step2")}
  14. +
  15. {t("quickUsage.step3")}
  16. +
  17. {t("quickUsage.step4")}
  18. +
  19. {t("quickUsage.step5")}
diff --git a/web/src/components/shell-result.tsx b/web/src/components/shell-result.tsx index aeaa14f3..226e7240 100644 --- a/web/src/components/shell-result.tsx +++ b/web/src/components/shell-result.tsx @@ -26,6 +26,8 @@ import { } from "@/types/shell.ts"; import { CircleHelpIcon, TriangleAlertIcon } from "lucide-react"; import { Fragment } from "react"; +import { useTranslation } from "react-i18next"; +import { toast } from "sonner"; function AgentResult({ packResult, generateResult }: { packResult: string; generateResult?: GenerateResult }) { return ( @@ -77,25 +79,26 @@ function AgentResult({ packResult, generateResult }: { packResult: string; gener } function FeedbackAlert() { + const { t } = useTranslation(); return ( - 内存马利用失败 ? + {t("shellNotWork.title")}
    -
  1. 1. 尝试开启调试模式,重新生成内存马并注入,查看控制台或日志
  2. -
  3. 2. 如果出现异常堆栈信息,或未见异常,请截图当前生成界面以及异常日志,并尽可能描述目标环境进行反馈
  4. +
  5. {t("shellNotWork.step1")}
  6. +
  7. {t("shellNotWork.step2")}
- 取消 + {t("cancel")} window.open( @@ -103,7 +106,7 @@ function FeedbackAlert() { ) } > - 反馈 + {t("feedback")}
@@ -112,36 +115,40 @@ function FeedbackAlert() { } function BasicInfo({ generateResult }: { generateResult?: GenerateResult }) { + const { t } = useTranslation(); return ( - 基础信息 + {t("generateResult.basicInfo")}
- - + +
- + {generateResult?.shellConfig.shellTool === "Behinder" && ( - - + + @@ -150,19 +157,19 @@ function BasicInfo({ generateResult }: { generateResult?: GenerateResult }) { {generateResult?.shellConfig.shellTool === "Godzilla" && ( - - + + @@ -171,19 +178,19 @@ function BasicInfo({ generateResult }: { generateResult?: GenerateResult }) { {generateResult?.shellConfig.shellTool === "Command" && ( )} @@ -199,81 +206,95 @@ export function ShellResult({ }: { packResult: string; packMethod: string; generateResult?: GenerateResult }) { const showCode = packMethod === "JSP"; const isAgent = packMethod.startsWith("Agent"); + const { t } = useTranslation(); return ( - - - 打包结果 - 内存马类 - 注入器类 - - -
- {generateResult && } - {!generateResult && } -
- {!isAgent && ( - - )} - {isAgent && } -
- - - - Warning - 反编译还在开发中,因此当前仅能看到 base64 编码格式 - -
- {generateResult && ( - - )} -
- -
- - - - Warning - 反编译还在开发中,因此当前仅能看到 base64 编码格式 - -
- {generateResult && ( - - )} -
- -
-
+ + {generateResult ? ( + + + {t("generateResult.title1")} + {t("generateResult.title2")} + {t("generateResult.title3")} + + +
+ +
+ {!isAgent && ( + + )} + {isAgent && } +
+ + + + Warning + {t("tips.decompileTip")} + +
+ +
+ +
+ + + + Warning + {t("tips.decompileTip")} + +
+ +
+ +
+
+ ) : ( + + )} +
); } diff --git a/web/src/components/tips/jre-tip.tsx b/web/src/components/tips/jre-tip.tsx index 2fdad5ed..68cb20cf 100644 --- a/web/src/components/tips/jre-tip.tsx +++ b/web/src/components/tips/jre-tip.tsx @@ -1,7 +1,9 @@ import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip.tsx"; import { InfoIcon } from "lucide-react"; +import { useTranslation } from "react-i18next"; export function JreTip() { + const { t } = useTranslation(); return ( @@ -9,8 +11,8 @@ export function JreTip() { -

目标 JRE 版本,一般而言为了最大的兼容性,默认 Java 6 即可,Java 高版本能加载低版本的字节码。

-

特定情况下,例如 JDK8 才能使用 lambda 表达式,JDK9 以上存在模块限制时才需要选择特定的版本。

+

{t("tips.jreTip")}

+

{t("tips.jreTip2")}

diff --git a/web/src/components/tips/url-pattern-tip.tsx b/web/src/components/tips/url-pattern-tip.tsx index 2f3985a9..b779bd90 100644 --- a/web/src/components/tips/url-pattern-tip.tsx +++ b/web/src/components/tips/url-pattern-tip.tsx @@ -1,7 +1,9 @@ import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip.tsx"; import { InfoIcon } from "lucide-react"; +import { useTranslation } from "react-i18next"; export function UrlPatternTip() { + const { t } = useTranslation(); return ( @@ -9,10 +11,9 @@ export function UrlPatternTip() { -

当使用 Servlet 内存马时必须写具体的 urlPattern,不能使用 /*,不然无法使用

-

当使用 SpringMVC ControllerHandler 内存马时必须写具体的 urlPattern,不能使用 /*,不然无法使用

-

当使用 SpringWebFlux HandlerMethod 内存马时必须写具体的 urlPattern,不能使用 /*,不然无法使用

-

当使用 SpringWebFlux HandlerFunction 内存马时必须写具体的 urlPattern,不能使用 /*,不然无法使用

+

{t("tips.servletUrlPattern")}

+

{t("tips.controllerUrlPattern")}

+

{t("tips.handlerUrlPattern")}

diff --git a/web/src/i18n/i18n.ts b/web/src/i18n/i18n.ts new file mode 100644 index 00000000..d7c8cc1d --- /dev/null +++ b/web/src/i18n/i18n.ts @@ -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; diff --git a/web/src/i18n/translations.ts b/web/src/i18n/translations.ts new file mode 100644 index 00000000..182d1f4b --- /dev/null +++ b/web/src/i18n/translations.ts @@ -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}}", + }, + }, + }, +}; diff --git a/web/src/lib/utils.ts b/web/src/lib/utils.ts index 6b43cf07..050b799d 100644 --- a/web/src/lib/utils.ts +++ b/web/src/lib/utils.ts @@ -1,16 +1,11 @@ import { type ClassValue, clsx } from "clsx"; -import { toast } from "sonner"; import { twMerge } from "tailwind-merge"; export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)); } -export function downloadBytes(base64String?: string, className?: string, jarName?: string) { - if (!base64String) { - toast.warning("字节码为空,无法下载, 请先生成内存马"); - return; - } +export function downloadBytes(base64String: string, className?: string, jarName?: string) { const byteCharacters = atob(base64String); const byteNumbers = new Array(byteCharacters.length); for (let i = 0; i < byteCharacters.length; i++) { diff --git a/web/src/main.tsx b/web/src/main.tsx index 969f6d6e..e41a48fc 100644 --- a/web/src/main.tsx +++ b/web/src/main.tsx @@ -6,6 +6,8 @@ import { TailwindIndicator } from "@/components/tailwind-indicator.tsx"; import { Toaster } from "@/components/ui/sonner.tsx"; import { env } from "@/config.ts"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import { I18nextProvider } from "react-i18next"; +import i18n from "./i18n/i18n"; const queryClient = new QueryClient(); @@ -26,9 +28,11 @@ if (!rootElement.innerHTML) { const root = ReactDOM.createRoot(rootElement); root.render( - - - {env.MODE !== "production" && } + + + + {env.MODE !== "production" && } + , ); } diff --git a/web/src/routes/__root.tsx b/web/src/routes/__root.tsx index 75dfad69..9f7abae0 100644 --- a/web/src/routes/__root.tsx +++ b/web/src/routes/__root.tsx @@ -1,3 +1,4 @@ +import { LanguageSwitcher } from "@/components/language-switcher"; import { ModeToggle } from "@/components/mode-toggle.tsx"; import { ThemeProvider } from "@/components/theme-provider.tsx"; import { Button } from "@/components/ui/button.tsx"; @@ -17,6 +18,7 @@ function RootComponent() {

MemShellParty - JavaWeb

+
diff --git a/web/src/types/shell.ts b/web/src/types/shell.ts index 2146e9ed..7047ec41 100644 --- a/web/src/types/shell.ts +++ b/web/src/types/shell.ts @@ -58,9 +58,7 @@ export interface MainConfig { }; } -export interface PackerConfig { - [packerName: string]: string; -} +export type PackerConfig = Array; export interface GenerateResponse { packResult: string;