style: lint and fmt code

This commit is contained in:
ReaJason
2026-08-30 21:19:42 +08:00
parent 3ada01bd97
commit e1152000f2
24 changed files with 1247 additions and 133 deletions
+7 -7
View File
@@ -1,12 +1,6 @@
{ {
"$schema": "./node_modules/oxfmt/configuration_schema.json", "$schema": "./node_modules/oxfmt/configuration_schema.json",
"experimentalTailwindcss": { "ignorePatterns": ["content/**"],
"stylesheet": "./app/app.css",
"attributes": ["class", "className"],
"functions": ["clsx", "cn"],
"preserveWhitespace": true
},
"ignorePatterns": [],
"sortImports": { "sortImports": {
"newlinesBetween": true, "newlinesBetween": true,
"groups": [ "groups": [
@@ -16,5 +10,11 @@
["value-parent", "value-sibling", "value-index"], ["value-parent", "value-sibling", "value-index"],
"unknown" "unknown"
] ]
},
"sortTailwindcss": {
"stylesheet": "./app/app.css",
"attributes": ["class", "className"],
"functions": ["clsx", "cn"],
"preserveWhitespace": true
} }
} }
+1
View File
@@ -1,5 +1,6 @@
{ {
"$schema": "./node_modules/oxlint/configuration_schema.json", "$schema": "./node_modules/oxlint/configuration_schema.json",
"ignorePatterns": ["vendor/**"],
"rules": { "rules": {
"no-unused-vars": [ "no-unused-vars": [
"warn", "warn",
@@ -23,6 +23,7 @@ import { FeedbackAlert } from "./feedback-alert";
export function BasicInfo({ generateResult }: Readonly<{ generateResult?: MemShellResult }>) { export function BasicInfo({ generateResult }: Readonly<{ generateResult?: MemShellResult }>) {
const { t } = useTranslation(["memshell", "common"]); const { t } = useTranslation(["memshell", "common"]);
const isDubbo = generateResult?.shellConfig.server === "Dubbo"; const isDubbo = generateResult?.shellConfig.server === "Dubbo";
const shellToolConfig = generateResult?.shellToolConfig;
return ( return (
<Card> <Card>
<CardHeader> <CardHeader>
@@ -65,13 +66,13 @@ export function BasicInfo({ generateResult }: Readonly<{ generateResult?: MemShe
/> />
<CopyableField <CopyableField
label={t("shellToolConfig.behinder.pass")} label={t("shellToolConfig.behinder.pass")}
text={(generateResult?.shellToolConfig as BehinderShellToolConfig).pass} text={(shellToolConfig as BehinderShellToolConfig).pass}
value={(generateResult?.shellToolConfig as BehinderShellToolConfig).pass} value={(shellToolConfig as BehinderShellToolConfig).pass}
/> />
<CopyableField <CopyableField
label={t("shellToolConfig.behinder.header")} label={t("shellToolConfig.behinder.header")}
text={`${(generateResult?.shellToolConfig as BehinderShellToolConfig).headerName}: ${(generateResult?.shellToolConfig as BehinderShellToolConfig).headerValue}`} text={`${(shellToolConfig as BehinderShellToolConfig).headerName}: ${(shellToolConfig as BehinderShellToolConfig).headerValue}`}
value={`${(generateResult?.shellToolConfig as BehinderShellToolConfig).headerName}: ${(generateResult?.shellToolConfig as BehinderShellToolConfig).headerValue}`} value={`${(shellToolConfig as BehinderShellToolConfig).headerName}: ${(shellToolConfig as BehinderShellToolConfig).headerValue}`}
/> />
</> </>
)} )}
@@ -79,26 +80,28 @@ export function BasicInfo({ generateResult }: Readonly<{ generateResult?: MemShe
<> <>
<CopyableField <CopyableField
label={t("shellToolConfig.godzilla.pass")} label={t("shellToolConfig.godzilla.pass")}
text={(generateResult?.shellToolConfig as GodzillaShellToolConfig).pass} text={(shellToolConfig as GodzillaShellToolConfig).pass}
value={(generateResult?.shellToolConfig as GodzillaShellToolConfig).pass} value={(shellToolConfig as GodzillaShellToolConfig).pass}
/> />
<CopyableField <CopyableField
label={t("shellToolConfig.godzilla.key")} label={t("shellToolConfig.godzilla.key")}
text={(generateResult?.shellToolConfig as GodzillaShellToolConfig).key} text={(shellToolConfig as GodzillaShellToolConfig).key}
value={(generateResult?.shellToolConfig as GodzillaShellToolConfig).key} value={(shellToolConfig as GodzillaShellToolConfig).key}
/> />
<CopyableField <CopyableField
label={t("shellToolConfig.godzilla.encryptor")} label={t("shellToolConfig.godzilla.encryptor")}
text={ text={
generateResult?.shellConfig.shellType.includes("WebSocket") generateResult?.shellConfig.shellType.includes("WebSocket")
? "JAVA_WEBSOCKET_AES_RAW" ? "JAVA_WEBSOCKET_AES_RAW"
: generateResult?.shellConfig.shellType.includes("Dubbo") ? "DUBBO_XOR_BASE64" : "JAVA_AES_BASE64" : generateResult?.shellConfig.shellType.includes("Dubbo")
? "DUBBO_XOR_BASE64"
: "JAVA_AES_BASE64"
} }
/> />
<CopyableField <CopyableField
label={t("shellToolConfig.godzilla.header")} label={t("shellToolConfig.godzilla.header")}
text={`${(generateResult?.shellToolConfig as GodzillaShellToolConfig).headerName}: ${(generateResult?.shellToolConfig as GodzillaShellToolConfig).headerValue}`} text={`${(shellToolConfig as GodzillaShellToolConfig).headerName}: ${(shellToolConfig as GodzillaShellToolConfig).headerValue}`}
value={`${(generateResult?.shellToolConfig as GodzillaShellToolConfig).headerName}: ${(generateResult?.shellToolConfig as GodzillaShellToolConfig).headerValue}`} value={`${(shellToolConfig as GodzillaShellToolConfig).headerName}: ${(shellToolConfig as GodzillaShellToolConfig).headerValue}`}
/> />
</> </>
)} )}
@@ -107,8 +110,8 @@ export function BasicInfo({ generateResult }: Readonly<{ generateResult?: MemShe
<CopyableField <CopyableField
hidden={generateResult?.shellConfig.shellType.includes("WebSocket")} hidden={generateResult?.shellConfig.shellType.includes("WebSocket")}
label={t("common:paramName")} label={t("common:paramName")}
text={(generateResult?.shellToolConfig as CommandShellToolConfig).paramName} text={(shellToolConfig as CommandShellToolConfig).paramName}
value={(generateResult?.shellToolConfig as CommandShellToolConfig).paramName} value={(shellToolConfig as CommandShellToolConfig).paramName}
/> />
<CopyableField <CopyableField
hidden={ hidden={
@@ -118,8 +121,8 @@ export function BasicInfo({ generateResult }: Readonly<{ generateResult?: MemShe
) )
} }
label={t("shellToolConfig.httpHeader")} label={t("shellToolConfig.httpHeader")}
text={`${(generateResult?.shellToolConfig as CommandShellToolConfig).headerName}: ${(generateResult?.shellToolConfig as CommandShellToolConfig).headerValue}`} text={`${(shellToolConfig as CommandShellToolConfig).headerName}: ${(shellToolConfig as CommandShellToolConfig).headerValue}`}
value={`${(generateResult?.shellToolConfig as CommandShellToolConfig).headerName}: ${(generateResult?.shellToolConfig as CommandShellToolConfig).headerValue}`} value={`${(shellToolConfig as CommandShellToolConfig).headerName}: ${(shellToolConfig as CommandShellToolConfig).headerValue}`}
/> />
</Fragment> </Fragment>
)} )}
@@ -127,28 +130,28 @@ export function BasicInfo({ generateResult }: Readonly<{ generateResult?: MemShe
generateResult?.shellConfig.shellTool === ShellToolType.Suo5v2) && ( generateResult?.shellConfig.shellTool === ShellToolType.Suo5v2) && (
<CopyableField <CopyableField
label={t("shellToolConfig.suo5Header")} label={t("shellToolConfig.suo5Header")}
text={`${(generateResult?.shellToolConfig as Suo5ShellToolConfig).headerName}: ${(generateResult?.shellToolConfig as Suo5ShellToolConfig).headerValue}`} text={`${(shellToolConfig as Suo5ShellToolConfig).headerName}: ${(shellToolConfig as Suo5ShellToolConfig).headerValue}`}
value={`${(generateResult?.shellToolConfig as Suo5ShellToolConfig).headerName}: ${(generateResult?.shellToolConfig as Suo5ShellToolConfig).headerValue}`} value={`${(shellToolConfig as Suo5ShellToolConfig).headerName}: ${(shellToolConfig as Suo5ShellToolConfig).headerValue}`}
/> />
)} )}
{generateResult?.shellConfig.shellTool === ShellToolType.Proxy && ( {generateResult?.shellConfig.shellTool === ShellToolType.Proxy && (
<CopyableField <CopyableField
label={t("shellToolConfig.httpHeader")} label={t("shellToolConfig.httpHeader")}
text={`${(generateResult?.shellToolConfig as ProxyShellToolConfig).headerName}: ${(generateResult?.shellToolConfig as ProxyShellToolConfig).headerValue}`} text={`${(shellToolConfig as ProxyShellToolConfig).headerName}: ${(shellToolConfig as ProxyShellToolConfig).headerValue}`}
value={`${(generateResult?.shellToolConfig as ProxyShellToolConfig).headerName}: ${(generateResult?.shellToolConfig as ProxyShellToolConfig).headerValue}`} value={`${(shellToolConfig as ProxyShellToolConfig).headerName}: ${(shellToolConfig as ProxyShellToolConfig).headerValue}`}
/> />
)} )}
{generateResult?.shellConfig.shellTool === ShellToolType.AntSword && ( {generateResult?.shellConfig.shellTool === ShellToolType.AntSword && (
<> <>
<CopyableField <CopyableField
label={t("shellToolConfig.antSword.pass")} label={t("shellToolConfig.antSword.pass")}
text={(generateResult?.shellToolConfig as AntSwordShellToolConfig).pass} text={(shellToolConfig as AntSwordShellToolConfig).pass}
value={(generateResult?.shellToolConfig as AntSwordShellToolConfig).pass} value={(shellToolConfig as AntSwordShellToolConfig).pass}
/> />
<CopyableField <CopyableField
label={t("shellToolConfig.httpHeader")} label={t("shellToolConfig.httpHeader")}
text={`${(generateResult?.shellToolConfig as AntSwordShellToolConfig).headerName}: ${(generateResult?.shellToolConfig as AntSwordShellToolConfig).headerValue}`} text={`${(shellToolConfig as AntSwordShellToolConfig).headerName}: ${(shellToolConfig as AntSwordShellToolConfig).headerValue}`}
value={`${(generateResult?.shellToolConfig as AntSwordShellToolConfig).headerName}: ${(generateResult?.shellToolConfig as AntSwordShellToolConfig).headerValue}`} value={`${(shellToolConfig as AntSwordShellToolConfig).headerName}: ${(shellToolConfig as AntSwordShellToolConfig).headerValue}`}
/> />
</> </>
)} )}
@@ -157,8 +160,8 @@ export function BasicInfo({ generateResult }: Readonly<{ generateResult?: MemShe
<CopyableField label={t("shellToolConfig.neoreGeorgKey")} text="key" value="key" /> <CopyableField label={t("shellToolConfig.neoreGeorgKey")} text="key" value="key" />
<CopyableField <CopyableField
label={t("shellToolConfig.neoreGeorgHeader")} label={t("shellToolConfig.neoreGeorgHeader")}
text={`${(generateResult?.shellToolConfig as NeoreGeorgShellToolConfig).headerName}: ${(generateResult?.shellToolConfig as NeoreGeorgShellToolConfig).headerValue}`} text={`${(shellToolConfig as NeoreGeorgShellToolConfig).headerName}: ${(shellToolConfig as NeoreGeorgShellToolConfig).headerValue}`}
value={`${(generateResult?.shellToolConfig as NeoreGeorgShellToolConfig).headerName}: ${(generateResult?.shellToolConfig as NeoreGeorgShellToolConfig).headerValue}`} value={`${(shellToolConfig as NeoreGeorgShellToolConfig).headerName}: ${(shellToolConfig as NeoreGeorgShellToolConfig).headerValue}`}
/> />
</> </>
)} )}
+1 -1
View File
@@ -4,7 +4,7 @@ import { DownloadIcon } from "lucide-react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { toast } from "sonner"; import { toast } from "sonner";
import DecompiledCodeViewer from "@/components/memshell/decompiled-code-viewer"; import DecompiledCodeViewer from "@/components/decompiled-code-viewer";
import { QuickUsage } from "@/components/memshell/quick-usage"; import { QuickUsage } from "@/components/memshell/quick-usage";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
+10 -10
View File
@@ -12,6 +12,7 @@ export function BasicInfo({ generateResult }: Readonly<{ generateResult?: ProbeS
const isBodyContent = generateResult?.probeConfig.probeMethod === "ResponseBody"; const isBodyContent = generateResult?.probeConfig.probeMethod === "ResponseBody";
const isFilterContent = generateResult?.probeConfig.probeContent === "Filter"; const isFilterContent = generateResult?.probeConfig.probeContent === "Filter";
const isBodyCommand = isBodyContent && generateResult?.probeConfig.probeContent === "Command"; const isBodyCommand = isBodyContent && generateResult?.probeConfig.probeContent === "Command";
const probeContentConfig = generateResult?.probeContentConfig as ResponseBodyConfig | undefined;
return ( return (
<Card> <Card>
<CardHeader> <CardHeader>
@@ -28,18 +29,17 @@ export function BasicInfo({ generateResult }: Readonly<{ generateResult?: ProbeS
{!isFilterContent && isBodyContent && ( {!isFilterContent && isBodyContent && (
<CopyableField <CopyableField
label={t("common:paramName")} label={t("common:paramName")}
value={(generateResult?.probeContentConfig as ResponseBodyConfig).reqParamName} value={probeContentConfig?.reqParamName}
text={(generateResult?.probeContentConfig as ResponseBodyConfig).reqParamName} text={probeContentConfig?.reqParamName}
/>
)}
{isBodyCommand && probeContentConfig?.commandTemplate && (
<CopyableField
label={t("common:commandTemplate")}
value={probeContentConfig.commandTemplate}
text={probeContentConfig.commandTemplate}
/> />
)} )}
{isBodyCommand &&
(generateResult?.probeContentConfig as ResponseBodyConfig).commandTemplate && (
<CopyableField
label={t("common:commandTemplate")}
value={(generateResult?.probeContentConfig as ResponseBodyConfig).commandTemplate}
text={(generateResult?.probeContentConfig as ResponseBodyConfig).commandTemplate}
/>
)}
<CopyableField <CopyableField
label={t("probeshell:shellClassName")} label={t("probeshell:shellClassName")}
value={generateResult?.shellClassName} value={generateResult?.shellClassName}
+61 -2
View File
@@ -1,9 +1,15 @@
import type { ProbeShellResult } from "@/types/probeshell"; import type { ProbeShellResult } from "@/types/probeshell";
import { DownloadIcon } from "lucide-react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { toast } from "sonner";
import DecompiledCodeViewer from "@/components/decompiled-code-viewer";
import { QuickUsage } from "@/components/probeshell/quick-usage"; import { QuickUsage } from "@/components/probeshell/quick-usage";
import { Button } from "@/components/ui/button";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { useDecompiledSources } from "@/lib/decompile/use-decompiled-sources";
import { downloadBytes } from "@/lib/utils";
import CodeViewer from "../code-viewer"; import CodeViewer from "../code-viewer";
import { BasicInfo } from "./basic-info"; import { BasicInfo } from "./basic-info";
@@ -17,16 +23,29 @@ export default function ShellResult({
packMethod: string; packMethod: string;
generateResult?: ProbeShellResult; generateResult?: ProbeShellResult;
}>) { }>) {
const { t } = useTranslation(); const { t } = useTranslation(["common", "probeshell"]);
const { sources, isDecompiling, error } = useDecompiledSources(generateResult);
if (!generateResult) { if (!generateResult) {
return <QuickUsage />; return <QuickUsage />;
} }
const showCode = packMethod === "JSP"; const showCode = packMethod === "JSP";
const height = 600; const height = 600;
const decompiledSourceHeight = 800;
const shellClassName = generateResult.shellClassName;
const shellBytesBase64 = generateResult.shellBytesBase64Str;
const sourcePlaceholder = isDecompiling
? `// ${t("common:decompiling")}`
: error
? `// ${t("common:decompileFailed", { error })}`
: "";
return ( return (
<Tabs defaultValue="packResult"> <Tabs defaultValue="packResult">
<TabsList className="grid w-full grid-cols-1"> <TabsList className="grid w-full grid-cols-2">
<TabsTrigger value="packResult">{t("common:generateResult")}</TabsTrigger> <TabsTrigger value="packResult">{t("common:generateResult")}</TabsTrigger>
<TabsTrigger value="shell">{t("probeshell:shellClass")}</TabsTrigger>
</TabsList> </TabsList>
<TabsContent value="packResult" className="space-y-2"> <TabsContent value="packResult" className="space-y-2">
<BasicInfo generateResult={generateResult} /> <BasicInfo generateResult={generateResult} />
@@ -48,6 +67,46 @@ export default function ShellResult({
/> />
)} )}
</TabsContent> </TabsContent>
<TabsContent value="shell" className="mt-4" keepMounted>
<DecompiledCodeViewer
copyLabel={t("common:copy")}
copyOptions={[
{
label: t("probeshell:copySource"),
value: sources.shell?.source ?? "",
disabled: !sources.shell,
},
{
label: t("probeshell:copyBase64"),
value: shellBytesBase64 ?? "",
disabled: !shellBytesBase64,
},
]}
header={<div className="truncate text-xs">{shellClassName}</div>}
button={
<Button
variant="ghost"
size="icon"
type="button"
className="h-7 w-7 [&_svg]:h-4 [&_svg]:w-4"
aria-label={t("common:download")}
title={t("common:download")}
onClick={() => {
if (!shellBytesBase64) {
toast.warning(t("probeshell:tips.shellBytesEmpty"));
return;
}
downloadBytes(shellBytesBase64, shellClassName);
}}
>
<DownloadIcon className="h-4 w-4" />
</Button>
}
height={decompiledSourceHeight}
lines={sources.shell?.lines ?? null}
placeholder={sourcePlaceholder}
/>
</TabsContent>
</Tabs> </Tabs>
); );
} }
+8 -12
View File
@@ -1,13 +1,9 @@
import * as React from "react" import { ScrollArea as ScrollAreaPrimitive } from "@base-ui/react/scroll-area";
import { ScrollArea as ScrollAreaPrimitive } from "@base-ui/react/scroll-area" import * as React from "react";
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils";
function ScrollArea({ function ScrollArea({ className, children, ...props }: ScrollAreaPrimitive.Root.Props) {
className,
children,
...props
}: ScrollAreaPrimitive.Root.Props) {
return ( return (
<ScrollAreaPrimitive.Root <ScrollAreaPrimitive.Root
data-slot="scroll-area" data-slot="scroll-area"
@@ -23,7 +19,7 @@ function ScrollArea({
<ScrollBar /> <ScrollBar />
<ScrollAreaPrimitive.Corner /> <ScrollAreaPrimitive.Corner />
</ScrollAreaPrimitive.Root> </ScrollAreaPrimitive.Root>
) );
} }
function ScrollBar({ function ScrollBar({
@@ -38,7 +34,7 @@ function ScrollBar({
orientation={orientation} orientation={orientation}
className={cn( className={cn(
"flex touch-none p-px transition-colors select-none data-horizontal:h-2.5 data-horizontal:flex-col data-horizontal:border-t data-horizontal:border-t-transparent data-vertical:h-full data-vertical:w-2.5 data-vertical:border-l data-vertical:border-l-transparent", "flex touch-none p-px transition-colors select-none data-horizontal:h-2.5 data-horizontal:flex-col data-horizontal:border-t data-horizontal:border-t-transparent data-vertical:h-full data-vertical:w-2.5 data-vertical:border-l data-vertical:border-l-transparent",
className className,
)} )}
{...props} {...props}
> >
@@ -47,7 +43,7 @@ function ScrollBar({
className="relative flex-1 rounded-full bg-border" className="relative flex-1 rounded-full bg-border"
/> />
</ScrollAreaPrimitive.Scrollbar> </ScrollAreaPrimitive.Scrollbar>
) );
} }
export { ScrollArea, ScrollBar } export { ScrollArea, ScrollBar };
+1
View File
@@ -1,5 +1,6 @@
{ {
"about": "About", "about": "About",
"documents": "Documents",
"basicInfo": "BasicInfo", "basicInfo": "BasicInfo",
"byPassJavaModule": "BypassModule", "byPassJavaModule": "BypassModule",
"byPassJavaModule.description": "JDK 9+ module system strictly restricts reflective defineClass calls. When enabled, automatically inserts Unsafe-based module bypass code into injector", "byPassJavaModule.description": "JDK 9+ module system strictly restricts reflective defineClass calls. When enabled, automatically inserts Unsafe-based module bypass code into injector",
+1
View File
@@ -1,5 +1,6 @@
{ {
"about": "关于", "about": "关于",
"documents": "文档",
"basicInfo": "基本信息", "basicInfo": "基本信息",
"byPassJavaModule": "绕过模块限制", "byPassJavaModule": "绕过模块限制",
"byPassJavaModule.description": "JDK 9+ 模块化系统严格限制反射调用 defineClass。开启后会在注入器中自动插入使用 Unsafe 绕过模块限制的代码", "byPassJavaModule.description": "JDK 9+ 模块化系统严格限制反射调用 defineClass。开启后会在注入器中自动插入使用 Unsafe 绕过模块限制的代码",
+5 -1
View File
@@ -1,5 +1,7 @@
{ {
"buttons.generate": "Generate Shell", "buttons.generate": "Generate Shell",
"copyBase64": "Copy Base64",
"copySource": "Copy Source",
"dnslog.host": "DNSLog Host", "dnslog.host": "DNSLog Host",
"probeContent": "ProbeContent", "probeContent": "ProbeContent",
"probeContent.bytecode": "Bytecode", "probeContent.bytecode": "Bytecode",
@@ -12,9 +14,11 @@
"quickUsage.step1": "Select Probe Method, DNSLog, Response, etc.", "quickUsage.step1": "Select Probe Method, DNSLog, Response, etc.",
"quickUsage.step2": "Select Probe Content, JDK, Server, etc.", "quickUsage.step2": "Select Probe Content, JDK, Server, etc.",
"quickUsage.step3": "Click Generate Shell", "quickUsage.step3": "Click Generate Shell",
"shellClass": "ProbeShell",
"shellClassName": "ShellClassName", "shellClassName": "ShellClassName",
"sleepSeconds": "SleepTime (seconds)", "sleepSeconds": "SleepTime (seconds)",
"sleepServer": "SleepServer", "sleepServer": "SleepServer",
"tips.dnslog.host.required": "DNSLog host required", "tips.dnslog.host.required": "DNSLog host required",
"tips.response.reqParamName.required": "ParamName required" "tips.response.reqParamName.required": "ParamName required",
"tips.shellBytesEmpty": "Shell bytes is empty, please generate shell first"
} }
+5 -1
View File
@@ -1,5 +1,7 @@
{ {
"buttons.generate": "生成探测马", "buttons.generate": "生成探测马",
"copyBase64": "复制 Base64",
"copySource": "复制源码",
"dnslog.host": "DNSLog 地址", "dnslog.host": "DNSLog 地址",
"probeContent": "探测内容", "probeContent": "探测内容",
"probeContent.bytecode": "字节码执行", "probeContent.bytecode": "字节码执行",
@@ -12,9 +14,11 @@
"quickUsage.step1": "选择探测方法,DNSLog、Response 或者其他", "quickUsage.step1": "选择探测方法,DNSLog、Response 或者其他",
"quickUsage.step2": "选择探测内容,JDK、服务类型 或者其他", "quickUsage.step2": "选择探测内容,JDK、服务类型 或者其他",
"quickUsage.step3": "点击生成探测马", "quickUsage.step3": "点击生成探测马",
"shellClass": "探测马",
"shellClassName": "探测马类名", "shellClassName": "探测马类名",
"sleepSeconds": "延迟时间(秒)", "sleepSeconds": "延迟时间(秒)",
"sleepServer": "探测服务", "sleepServer": "探测服务",
"tips.dnslog.host.required": "DNSLog 地址必填", "tips.dnslog.host.required": "DNSLog 地址必填",
"tips.response.reqParamName.required": "参数名称必填" "tips.response.reqParamName.required": "参数名称必填",
"tips.shellBytesEmpty": "探测马字节码为空,无法下载,请先生成探测马"
} }
+15 -4
View File
@@ -1,7 +1,17 @@
import type { LinkItemType } from "fumadocs-ui/layouts/shared"; import type { LinkItemType } from "fumadocs-ui/layouts/shared";
import { useTranslation } from "react-i18next";
import { LanguageSwitcher } from "@/components/language-switcher"; import { LanguageSwitcher } from "@/components/language-switcher";
type NavLabelKey = "MemShellGenerator" | "ProbeShellGenerator" | "documents" | "about";
function NavLabel({ translationKey }: { translationKey: NavLabelKey }) {
const { t } = useTranslation("common");
return t(translationKey);
}
export const siteConfig = { export const siteConfig = {
name: "MemShellParty", name: "MemShellParty",
url: "https://party.memshell.news", url: "https://party.memshell.news",
@@ -13,19 +23,20 @@ export const siteConfig = {
blog: "https://reajason.eu.org", blog: "https://reajason.eu.org",
navLinks: [ navLinks: [
{ {
text: "MemShellGenerator", text: <NavLabel translationKey="MemShellGenerator" />,
url: "/memshell", url: "/memshell",
}, },
{ {
text: "ProbeShellGenerator", text: <NavLabel translationKey="ProbeShellGenerator" />,
url: "/probeshell", url: "/probeshell",
}, },
{ {
text: "Documents", text: <NavLabel translationKey="documents" />,
url: "/docs", url: "/docs",
external: true,
}, },
{ {
text: "About", text: <NavLabel translationKey="about" />,
url: "/about", url: "/about",
}, },
{ {
+9 -4
View File
@@ -16,8 +16,8 @@ export interface DecompileRequest {
id: number; id: number;
shellClassName: string; shellClassName: string;
shellBytesBase64: string; shellBytesBase64: string;
injectorClassName: string; injectorClassName?: string;
injectorBytesBase64: string; injectorBytesBase64?: string;
} }
export interface DecompileResponse { export interface DecompileResponse {
@@ -95,10 +95,15 @@ self.onmessage = async (event: MessageEvent<DecompileRequest>) => {
try { try {
const classes = new Map<string, Uint8Array>([ const classes = new Map<string, Uint8Array>([
[toJvmClassName(shellClassName), base64ToBytes(shellBytesBase64)], [toJvmClassName(shellClassName), base64ToBytes(shellBytesBase64)],
[toJvmClassName(injectorClassName), base64ToBytes(injectorBytesBase64)],
]); ]);
const hasInjector = injectorClassName && injectorBytesBase64;
if (hasInjector) {
classes.set(toJvmClassName(injectorClassName), base64ToBytes(injectorBytesBase64));
}
const shell = await decompileClass(toJvmClassName(shellClassName), classes); const shell = await decompileClass(toJvmClassName(shellClassName), classes);
const injector = await decompileClass(toJvmClassName(injectorClassName), classes); const injector = hasInjector
? await decompileClass(toJvmClassName(injectorClassName), classes)
: undefined;
self.postMessage({ id, shell, injector } satisfies DecompileResponse); self.postMessage({ id, shell, injector } satisfies DecompileResponse);
} catch (error) { } catch (error) {
self.postMessage({ self.postMessage({
@@ -1,8 +1,15 @@
import type { DecompiledClass, DecompileRequest, DecompileResponse } from "./decompile.worker"; import type { DecompiledClass, DecompileRequest, DecompileResponse } from "./decompile.worker";
import type { MemShellResult } from "@/types/memshell";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
/** Structural subset shared by MemShellResult and ProbeShellResult */
export interface DecompileInput {
shellClassName?: string;
shellBytesBase64Str?: string;
injectorClassName?: string;
injectorBytesBase64Str?: string;
}
export interface DecompiledSources { export interface DecompiledSources {
shell: DecompiledClass | null; shell: DecompiledClass | null;
injector: DecompiledClass | null; injector: DecompiledClass | null;
@@ -67,10 +74,10 @@ function getWorker() {
const entry = pending.get(id); const entry = pending.get(id);
if (!entry) return; if (!entry) return;
pending.delete(id); pending.delete(id);
if (error || !shell || !injector) { if (error || !shell) {
entry.reject(new Error(error ?? "decompile failed")); entry.reject(new Error(error ?? "decompile failed"));
} else { } else {
entry.resolve({ shell, injector }); entry.resolve({ shell, injector: injector ?? null });
} }
}; };
worker.onerror = (event) => { worker.onerror = (event) => {
@@ -90,7 +97,7 @@ function decompileInWorker(request: Omit<DecompileRequest, "id">) {
}); });
} }
export function useDecompiledSources(generateResult: MemShellResult | undefined) { export function useDecompiledSources(generateResult: DecompileInput | undefined) {
const shellClassName = generateResult?.shellClassName; const shellClassName = generateResult?.shellClassName;
const shellBytesBase64 = generateResult?.shellBytesBase64Str; const shellBytesBase64 = generateResult?.shellBytesBase64Str;
const injectorClassName = generateResult?.injectorClassName; const injectorClassName = generateResult?.injectorClassName;
@@ -99,12 +106,12 @@ export function useDecompiledSources(generateResult: MemShellResult | undefined)
const [state, setState] = useState<DecompileState>(IDLE); const [state, setState] = useState<DecompileState>(IDLE);
useEffect(() => { useEffect(() => {
if (!shellClassName || !shellBytesBase64 || !injectorClassName || !injectorBytesBase64) { if (!shellClassName || !shellBytesBase64) {
setState(IDLE); setState(IDLE);
return; return;
} }
const key = cacheKey(shellBytesBase64, injectorBytesBase64); const key = cacheKey(shellBytesBase64, injectorBytesBase64 ?? "");
const cached = cacheGet(key); const cached = cacheGet(key);
if (cached) { if (cached) {
setState({ sources: cached, isDecompiling: false, error: null }); setState({ sources: cached, isDecompiling: false, error: null });
+3 -3
View File
@@ -16,11 +16,11 @@ export function downloadContent(content: Blob, fileName: string, fileExtension:
export function base64ToBytes(base64String: string) { export function base64ToBytes(base64String: string) {
const byteCharacters = atob(base64String); const byteCharacters = atob(base64String);
const byteNumbers = new Array(byteCharacters.length); const bytes = new Uint8Array(byteCharacters.length);
for (let i = 0; i < byteCharacters.length; i++) { for (let i = 0; i < byteCharacters.length; i++) {
byteNumbers[i] = byteCharacters.charCodeAt(i); bytes[i] = byteCharacters.charCodeAt(i);
} }
return new Uint8Array(byteNumbers); return bytes;
} }
export function downloadBytes(base64String: string, className?: string, jarName?: string) { export function downloadBytes(base64String: string, className?: string, jarName?: string) {
+1 -2
View File
@@ -15,6 +15,7 @@ import ShellResult from "@/components/probeshell/shell-result";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { env } from "@/config"; import { env } from "@/config";
import { siteConfig } from "@/lib/config"; import { siteConfig } from "@/lib/config";
import { baseOptions } from "@/lib/layout.shared";
import { import {
type ProbeShellFormSchema, type ProbeShellFormSchema,
probeShellFormSchema, probeShellFormSchema,
@@ -22,8 +23,6 @@ import {
} from "@/types/schema"; } from "@/types/schema";
import { transformToProbePostData } from "@/utils/transformer"; import { transformToProbePostData } from "@/utils/transformer";
import { baseOptions } from "@/lib/layout.shared";
export default function ProbeShellGenerator() { export default function ProbeShellGenerator() {
const { data: responseBodyServers } = useQuery<string[]>({ const { data: responseBodyServers } = useQuery<string[]>({
queryKey: ["probeResponseBodyServers"], queryKey: ["probeResponseBodyServers"],
+16 -5
View File
@@ -10,6 +10,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [v2.10.0](https://github.com/ReaJason/MemShellParty/releases/tag/v2.10.0) ## [v2.10.0](https://github.com/ReaJason/MemShellParty/releases/tag/v2.10.0)
### Added
1. (WEB)内存马/探测马支持查看 cfr 反编译源码(Thanks @4ra1n for [4ra1n/cfr-wasm](https://github.com/4ra1n/cfr-wasm)
2. 新增 FreeMarker SpEL/SpELJDK17 打包方式(Thanks @xcxmiku
3. 新增 XSLT 打包方式
### Fixed ### Fixed
1. 修复 BES 10/11 部分场景 context 找不到(#157 By @su18#156 Thanks @mssky9527 1. 修复 BES 10/11 部分场景 context 找不到(#157 By @su18#156 Thanks @mssky9527
@@ -18,6 +24,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
4. 修复 SpringBoot4 WebFlux WebFilter 注入失败 4. 修复 SpringBoot4 WebFlux WebFilter 注入失败
5. 修复自定义内存马传入 Java5 包含低版本指令时生成失败 5. 修复自定义内存马传入 Java5 包含低版本指令时生成失败
### Changed
1. 更新 fumadocs 版本和使用模式
2. 依赖更新
**Full Changelog:** [v2.9.0...v2.10.0](https://github.com/ReaJason/MemShellParty/compare/v2.9.0...v2.10.0) **Full Changelog:** [v2.9.0...v2.10.0](https://github.com/ReaJason/MemShellParty/compare/v2.9.0...v2.10.0)
## [v2.9.0](https://github.com/ReaJason/MemShellParty/releases/tag/v2.9.0) - 2026-08-03 ## [v2.9.0](https://github.com/ReaJason/MemShellParty/releases/tag/v2.9.0) - 2026-08-03
@@ -84,11 +95,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1. 添加 Alibaba Dubbo 和 Apache Dubbo Service 内存马,专门针对 Dubbo Provider 的场景(#32 By @ReaJason 1. 添加 Alibaba Dubbo 和 Apache Dubbo Service 内存马,专门针对 Dubbo Provider 的场景(#32 By @ReaJason
2. 为 Attacher 添加默认列举 Java 进程和 `all` 参数注入所有 Java 进程的代码(与 JMG Agent.jar 命令一致) 2. 为 Attacher 添加默认列举 Java 进程和 `all` 参数注入所有 Java 进程的代码(与 JMG Agent.jar 命令一致)
```bash ```bash
java -jar agent.jar # 列举所有 Java 进程 java -jar agent.jar # 列举所有 Java 进程
java -jar agent.jar <pid> # 注入指定 PID Java 进程 java -jar agent.jar <pid> # 注入指定 PID Java 进程
java -jar agent.jar all # 注入所有 Java 进程 java -jar agent.jar all # 注入所有 Java 进程
``` ```
3. 添加 Command Base64 Encryptor,方便测试 Base64 流量特征攻击 3. 添加 Command Base64 Encryptor,方便测试 Base64 流量特征攻击
### Fixed ### Fixed
+13 -13
View File
@@ -7,7 +7,7 @@ icon: Album
### 中间件以及框架 ### 中间件以及框架
| [Tomcat5 ~ 11](/docs/server-intro#tomcat) | [Jetty6 ~ 12](/docs/server-intro#jetty) | [GlassFish3 ~ 7](/docs/server-intro#glassfish) | [Payara5 ~ 7](/docs/server-intro#payara) | | [Tomcat5 ~ 11](/docs/server-intro#tomcat) | [Jetty6 ~ 12](/docs/server-intro#jetty) | [GlassFish3 ~ 8](/docs/server-intro#glassfish) | [Payara5 ~ 7](/docs/server-intro#payara) |
| --------------------------------------------- | ------------------------------------------- | -------------------------------------------------- | -------------------------------------------- | | --------------------------------------------- | ------------------------------------------- | -------------------------------------------------- | -------------------------------------------- |
| Servlet | Servlet | Filter | Filter | | Servlet | Servlet | Filter | Filter |
| Filter | Filter | Listener | Listener | | Filter | Filter | Listener | Listener |
@@ -42,20 +42,20 @@ icon: Album
| FilterManager - Agent | ServletContext - Agent | | FilterManager - Agent | ServletContext - Agent |
| [BES (9.5.x ~ 11.0.0)](/docs/server-intro#bes) | [TongWeb6 ~ 8](/docs/server-intro#tongweb) | [InforSuite AS (9 ~ 10)](/docs/server-intro#inforsuite) | | [BES (9.5.x ~ 11.0.0)](/docs/server-intro#bes) | [TongWeb6 ~ 8](/docs/server-intro#tongweb) | [InforSuite AS (9 ~ 10)](/docs/server-intro#inforsuite) |
| ------------------------------------- | ---------------------------------------------- | ------------------------------------------------------- | | ---------------------------------------------- | ---------------------------------------------- | ------------------------------------------------------- |
| Filter | Filter | Filter | | Filter | Filter | Filter |
| Listener | Listener | Listener | | Listener | Listener | Listener |
| Valve | Valve | Valve | | Valve | Valve | Valve |
| FilterChain - Agent | FilterChain - Agent | FilterChain - Agent | | FilterChain - Agent | FilterChain - Agent | FilterChain - Agent |
| ContextValve - Agent | ContextValve - Agent | ContextValve - Agent | | ContextValve - Agent | ContextValve - Agent | ContextValve - Agent |
| [Apusic AS (9 ~ 10)](/docs/server-intro#apusic) | [Primeton (6.5)](/docs/server-intro#primeton) | [Dubbo Provider](/docs/server-intro#dubbo) | | [Apusic AS (9 ~ 10)](/docs/server-intro#apusic) | [Primeton (6.5)](/docs/server-intro#primeton) | [Dubbo Provider](/docs/server-intro#dubbo) |
| ----------------------------------------------- | --------------------------------------------- | --------------------------------------------- | | ----------------------------------------------- | --------------------------------------------- | ------------------------------------------ |
| Servlet | Filter | DubboService | | Servlet | Filter | DubboService |
| Filter | Listener | | | Filter | Listener | |
| Listener | Valve | | | Listener | Valve | |
| FilterChain - Agent | FilterChain - Agent | | | FilterChain - Agent | FilterChain - Agent | |
| | ContextValve - Agent | | | | ContextValve - Agent | |
### 内存马功能 ### 内存马功能
+5 -1
View File
@@ -22,7 +22,11 @@ import { Tab, Tabs } from "fumadocs-ui/components/tabs";
</dependency> </dependency>
``` ```
</Tab> </Tab>
<Tab>```groovy implementation 'io.github.reajason:generator:2.2.0' ```</Tab> <Tab>
```groovy
implementation 'io.github.reajason:generator:2.2.0'
```
</Tab>
</Tabs> </Tabs>
2. 如果需要使用自带的丰富打包方式,需要额外添加 packer 模块 2. 如果需要使用自带的丰富打包方式,需要额外添加 packer 模块
+6 -6
View File
@@ -1,10 +1,10 @@
declare module "@run-slicer/cfr" { declare module "@run-slicer/cfr" {
export type Options = Record<string, string>; export type Options = Record<string, string>;
export interface Config { export interface Config {
source?: (name: string) => Uint8Array | null; source?: (name: string) => Uint8Array | null;
options?: Options; options?: Options;
} }
export function decompile(name: string, config?: Config): Promise<string>; export function decompile(name: string, config?: Config): Promise<string>;
} }
+9 -11
View File
@@ -1,19 +1,17 @@
const isNode = const isNode = typeof process !== "undefined" && process?.versions?.node != null;
typeof process !== "undefined" &&
process?.versions?.node != null;
const wasmPath = async () => { const wasmPath = async () => {
const url = new URL("./cfr.wasm", import.meta.url).href; const url = new URL("./cfr.wasm", import.meta.url).href;
return isNode ? await import("node:url").then((mod) => mod.fileURLToPath(url)) : url; return isNode ? await import("node:url").then((mod) => mod.fileURLToPath(url)) : url;
}; };
let decompileFunc = null; let decompileFunc = null;
export const decompile = async (name, options) => { export const decompile = async (name, options) => {
if (!decompileFunc) { if (!decompileFunc) {
const { load } = await import("./cfr.wasm-runtime.js"); const { load } = await import("./cfr.wasm-runtime.js");
const { exports } = await load(await wasmPath(), { noAutoImports: true }); const { exports } = await load(await wasmPath(), { noAutoImports: true });
decompileFunc = exports.decompile; decompileFunc = exports.decompile;
} }
return decompileFunc(name, options); return decompileFunc(name, options);
}; };
+1018 -8
View File
File diff suppressed because one or more lines are too long
+11 -11
View File
@@ -2,23 +2,23 @@
"name": "@run-slicer/cfr", "name": "@run-slicer/cfr",
"version": "0.1.6-0.152", "version": "0.1.6-0.152",
"description": "A JavaScript port of the CFR decompiler (https://github.com/leibnitz27/cfr).", "description": "A JavaScript port of the CFR decompiler (https://github.com/leibnitz27/cfr).",
"keywords": [
"cfr",
"decompilation",
"decompiler",
"java"
],
"license": "MIT",
"author": "run-slicer",
"type": "module", "type": "module",
"main": "cfr.js", "main": "cfr.js",
"module": "cfr.js", "module": "cfr.js",
"types": "cfr.d.ts",
"exports": { "exports": {
".": { ".": {
"types": "./cfr.d.ts", "types": "./cfr.d.ts",
"import": "./cfr.js", "import": "./cfr.js",
"default": "./cfr.js" "default": "./cfr.js"
} }
}, }
"types": "cfr.d.ts", }
"keywords": [
"decompiler",
"java",
"decompilation",
"cfr"
],
"author": "run-slicer",
"license": "MIT"
}