From 65d9a3c672dff2b70f88a1a832e9908d3f642fbc Mon Sep 17 00:00:00 2001 From: ReaJason Date: Sun, 16 Nov 2025 12:06:52 +0800 Subject: [PATCH] feat: support staticInitialize --- .../memshell/config/InjectorConfig.java | 5 ++++ .../memshell/generator/InjectorGenerator.java | 5 ++++ .../memshell/generator/ValveGenerator.java | 21 +++++++++----- .../javaweb/probe/config/ProbeConfig.java | 6 ++++ .../generator/ByteBuddyShellGenerator.java | 7 +++++ .../javaweb/integration/ProbeAssertion.java | 3 ++ .../javaweb/integration/ShellAssertion.java | 16 ++++++++--- web/bun.lockb | Bin 209452 -> 212284 bytes web/package.json | 26 +++++++++--------- .../components/memshell/main-config-card.tsx | 16 +++++++++++ .../probeshell/main-config-card.tsx | 18 ++++++++++++ web/src/i18n/common/en.json | 1 + web/src/i18n/common/zh-CN.json | 1 + web/src/pages/memshell.tsx | 1 + web/src/pages/probeshell.tsx | 1 + web/src/types/memshell.ts | 1 + web/src/types/probeshell.ts | 1 + web/src/types/schema.ts | 2 ++ web/src/utils/transformer.ts | 2 ++ 19 files changed, 109 insertions(+), 24 deletions(-) diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/config/InjectorConfig.java b/generator/src/main/java/com/reajason/javaweb/memshell/config/InjectorConfig.java index a5151b3e..5a85c011 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/config/InjectorConfig.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/config/InjectorConfig.java @@ -46,4 +46,9 @@ public class InjectorConfig { * 内存马类字节 */ private byte[] shellClassBytes; + + /** + * 添加静态代码块调用构造方法初始化 + */ + private boolean staticInitialize; } diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/generator/InjectorGenerator.java b/generator/src/main/java/com/reajason/javaweb/memshell/generator/InjectorGenerator.java index 480b15a4..cd197dd8 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/generator/InjectorGenerator.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/generator/InjectorGenerator.java @@ -3,6 +3,7 @@ package com.reajason.javaweb.memshell.generator; import com.reajason.javaweb.ClassBytesShrink; import com.reajason.javaweb.asm.InnerClassDiscovery; import com.reajason.javaweb.buddy.*; +import com.reajason.javaweb.memshell.ShellType; import com.reajason.javaweb.memshell.config.InjectorConfig; import com.reajason.javaweb.memshell.config.ShellConfig; import com.reajason.javaweb.utils.CommonUtil; @@ -59,6 +60,10 @@ public class InjectorGenerator { if (shellConfig.isDebugOff()) { builder = LogRemoveMethodVisitor.extend(builder); } + + if (injectorConfig.isStaticInitialize() && !shellConfig.getShellType().startsWith(ShellType.AGENT)) { + builder = StaticBlockSelfConstructorCall.extend(builder); + } return builder; } diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/generator/ValveGenerator.java b/generator/src/main/java/com/reajason/javaweb/memshell/generator/ValveGenerator.java index fbd2355b..97ab8e7b 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/generator/ValveGenerator.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/generator/ValveGenerator.java @@ -32,13 +32,20 @@ public class ValveGenerator { public static DynamicType.Builder build(DynamicType.Builder builder, AbstractServer shell, String serverVersion) { String packageName = null; - if (serverVersion.equals("6")) { - packageName = TONGWEB6_VALVE_PACKAGE; - } else if (serverVersion.equals("7")) { - packageName = TONGWEB7_VALVE_PACKAGE; - } else if (serverVersion.equals("8")) { - packageName = TONGWEB8_VALVE_PACKAGE; - } else if (shell instanceof Bes) { + if (serverVersion != null) { + switch (serverVersion) { + case "6": + packageName = TONGWEB6_VALVE_PACKAGE; + break; + case "7": + packageName = TONGWEB7_VALVE_PACKAGE; + break; + case "8": + packageName = TONGWEB8_VALVE_PACKAGE; + break; + } + } + if (shell instanceof Bes) { packageName = BES_VALVE_PACKAGE; } if (StringUtils.isEmpty(packageName)) { diff --git a/generator/src/main/java/com/reajason/javaweb/probe/config/ProbeConfig.java b/generator/src/main/java/com/reajason/javaweb/probe/config/ProbeConfig.java index 9983f308..7d817d6a 100644 --- a/generator/src/main/java/com/reajason/javaweb/probe/config/ProbeConfig.java +++ b/generator/src/main/java/com/reajason/javaweb/probe/config/ProbeConfig.java @@ -47,6 +47,12 @@ public class ProbeConfig { @Builder.Default private boolean shrink = false; + /** + * 是否添加静态代码块调用构造方法 + */ + @Builder.Default + private boolean staticInitialize = false; + public boolean isDebugOff() { return !debug; } diff --git a/generator/src/main/java/com/reajason/javaweb/probe/generator/ByteBuddyShellGenerator.java b/generator/src/main/java/com/reajason/javaweb/probe/generator/ByteBuddyShellGenerator.java index 2fc04fc7..b86c72b4 100644 --- a/generator/src/main/java/com/reajason/javaweb/probe/generator/ByteBuddyShellGenerator.java +++ b/generator/src/main/java/com/reajason/javaweb/probe/generator/ByteBuddyShellGenerator.java @@ -4,6 +4,7 @@ import com.reajason.javaweb.ClassBytesShrink; import com.reajason.javaweb.ShellGenerator; import com.reajason.javaweb.buddy.ByPassJavaModuleInterceptor; import com.reajason.javaweb.buddy.LogRemoveMethodVisitor; +import com.reajason.javaweb.buddy.StaticBlockSelfConstructorCall; import com.reajason.javaweb.probe.config.ProbeConfig; import com.reajason.javaweb.probe.config.ProbeContentConfig; import net.bytebuddy.ByteBuddy; @@ -27,13 +28,19 @@ public abstract class ByteBuddyShellGenerator impl @Override public byte[] getBytes() { DynamicType.Builder builder = build(new ByteBuddy()); + if (probeConfig.needByPassJavaModule()) { builder = ByPassJavaModuleInterceptor.extend(builder); } + if (probeConfig.isDebugOff()) { builder = LogRemoveMethodVisitor.extend(builder); } + if (probeConfig.isStaticInitialize()) { + builder = StaticBlockSelfConstructorCall.extend(builder); + } + try (DynamicType.Unloaded unloaded = builder.make()) { return ClassBytesShrink.shrink(unloaded.getBytes(), probeConfig.isShrink()); } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/ProbeAssertion.java b/integration-test/src/test/java/com/reajason/javaweb/integration/ProbeAssertion.java index 60945738..8f6036bb 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/ProbeAssertion.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/ProbeAssertion.java @@ -29,6 +29,7 @@ public class ProbeAssertion { .targetJreVersion(targetJreVersion) .debug(true) .shrink(true) + .staticInitialize(true) .build(); String reqParamName = "payload"; ResponseBodyConfig responseBodyConfig = ResponseBodyConfig.builder() @@ -57,6 +58,7 @@ public class ProbeAssertion { .targetJreVersion(targetJreVersion) .debug(true) .shrink(true) + .staticInitialize(true) .build(); String reqParamName = "payload"; ResponseBodyConfig responseBodyConfig = ResponseBodyConfig.builder() @@ -84,6 +86,7 @@ public class ProbeAssertion { .probeContent(ProbeContent.Command) .debug(true) .shrink(true) + .staticInitialize(true) .targetJreVersion(targetJreVersion) .build(); String headerName = "X-Header"; diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/ShellAssertion.java b/integration-test/src/test/java/com/reajason/javaweb/integration/ShellAssertion.java index 202d421a..7c07cae3 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/ShellAssertion.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/ShellAssertion.java @@ -84,13 +84,20 @@ public class ShellAssertion { @SneakyThrows public static void shellInjectIsOk(String url, String server, String shellType, String shellTool, int targetJdkVersion, Packers packer, GenericContainer appContainer, GenericContainer pythonContainer) { + shellInjectIsOk(url, server, null, shellType, shellTool, targetJdkVersion, packer, appContainer, pythonContainer); + } + + @SneakyThrows + public static void shellInjectIsOk(String url, String server, String serverVersion, String shellType, String shellTool, + int targetJdkVersion, Packers packer, + GenericContainer appContainer, GenericContainer pythonContainer) { Pair urls = getUrls(url, shellType, shellTool, packer); String shellUrl = urls.getLeft(); String urlPattern = urls.getRight(); ShellToolConfig shellToolConfig = getShellToolConfig(shellType, shellTool, packer); - MemShellResult generateResult = generate(urlPattern, server, shellType, shellTool, targetJdkVersion, shellToolConfig, packer); + MemShellResult generateResult = generate(urlPattern, server, serverVersion, shellType, shellTool, targetJdkVersion, shellToolConfig, packer); packerResultAndInject(generateResult, url, shellTool, shellType, packer, appContainer); @@ -107,7 +114,7 @@ public class ShellAssertion { String jarPath = "/" + shellTool + shellType + packer.name() + ".jar"; appContainer.copyFileToContainer(MountableFile.forHostPath(tempJar, 0100666), jarPath); FileUtils.deleteQuietly(tempJar.toFile()); - String pidInContainer = appContainer.execInContainer("bash", "/fetch_pid.sh").getStdout(); + String pidInContainer = appContainer.execInContainer("bash", "/fetch_pid.sh").getStdout().trim(); assertDoesNotThrow(() -> Long.parseLong(pidInContainer)); String stdout = appContainer.execInContainer("/jattach", pidInContainer, "load", "instrument", "false", jarPath).getStdout(); log.info("attach result: {}", stdout); @@ -291,8 +298,8 @@ public class ShellAssertion { return shellToolConfig; } - public static MemShellResult generate(String urlPattern, String server, String shellType, String shellTool, int targetJdkVersion, ShellToolConfig shellToolConfig, Packers packer) { - InjectorConfig injectorConfig = new InjectorConfig(); + public static MemShellResult generate(String urlPattern, String server, String serverVersin, String shellType, String shellTool, int targetJdkVersion, ShellToolConfig shellToolConfig, Packers packer) { + InjectorConfig injectorConfig = InjectorConfig.builder().staticInitialize(true).build(); if (StringUtils.isNotBlank(urlPattern)) { injectorConfig.setUrlPattern(urlPattern); } @@ -304,6 +311,7 @@ public class ShellAssertion { ShellConfig shellConfig = ShellConfig.builder() .server(server) + .serverVersion(serverVersin) .shellTool(shellTool) .shellType(shellType) .targetJreVersion(targetJdkVersion) diff --git a/web/bun.lockb b/web/bun.lockb index 9b05fd0796b52294f5ffd212c2164beed0c39b15..726e135221b45e7cea91f46f94858de527f224cd 100755 GIT binary patch delta 31924 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 BWQ_m- diff --git a/web/package.json b/web/package.json index ca1f9282..78d869ee 100644 --- a/web/package.json +++ b/web/package.json @@ -15,27 +15,27 @@ }, "devDependencies": { "@biomejs/biome": "2.1.4", - "@react-router/dev": "^7.9.5", - "@types/node": "^24.10.0", - "@types/react": "^19.2.2", + "@react-router/dev": "^7.9.6", + "@types/node": "^24.10.1", + "@types/react": "^19.2.5", "@types/react-copy-to-clipboard": "^5.0.7", - "@types/react-dom": "^19.2.2", + "@types/react-dom": "^19.2.3", "@types/react-syntax-highlighter": "^15.5.13", - "@vitejs/plugin-react": "^5.1.0", + "@vitejs/plugin-react": "^5.1.1", "rimraf": "^6.1.0", - "tailwindcss": "^4.1.16", + "tailwindcss": "^4.1.17", "typescript": "^5.9.3", - "vite": "^7.1.12", + "vite": "^7.2.2", "vite-bundle-visualizer": "^1.2.1" }, "dependencies": { "@hookform/resolvers": "^5.2.2", - "@tailwindcss/vite": "^4.1.16", - "@tanstack/react-query": "^5.90.6", + "@tailwindcss/vite": "^4.1.17", + "@tanstack/react-query": "^5.90.9", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "framer-motion": "^12.23.24", - "i18next": "^25.6.0", + "i18next": "^25.6.2", "lucide-react": "^0.539.0", "motion": "^12.23.24", "radix-ui": "^1.4.3", @@ -44,11 +44,11 @@ "react-dom": "^19.2.0", "react-hook-form": "^7.66.0", "react-i18next": "^15.7.4", - "react-router": "^7.9.5", - "react-router-dom": "^7.9.5", + "react-router": "^7.9.6", + "react-router-dom": "^7.9.6", "react-syntax-highlighter": "^15.6.6", "sonner": "^2.0.7", - "tailwind-merge": "^3.3.1", + "tailwind-merge": "^3.4.0", "tw-animate-css": "^1.4.0", "yup": "^1.7.1" }, diff --git a/web/src/components/memshell/main-config-card.tsx b/web/src/components/memshell/main-config-card.tsx index 4fcc9b32..b2fe2541 100644 --- a/web/src/components/memshell/main-config-card.tsx +++ b/web/src/components/memshell/main-config-card.tsx @@ -383,6 +383,22 @@ export default function MainConfigCard({ )} /> + ( + + + + + + + )} + /> diff --git a/web/src/components/probeshell/main-config-card.tsx b/web/src/components/probeshell/main-config-card.tsx index 168615b8..b7f98545 100644 --- a/web/src/components/probeshell/main-config-card.tsx +++ b/web/src/components/probeshell/main-config-card.tsx @@ -324,6 +324,24 @@ export default function MainConfigCard({ form, servers }: MainConfigCardProps) { )} /> + ( + + + + + + + )} + /> ), [form.control, t], diff --git a/web/src/i18n/common/en.json b/web/src/i18n/common/en.json index ea8f1db6..6d6477c8 100644 --- a/web/src/i18n/common/en.json +++ b/web/src/i18n/common/en.json @@ -28,6 +28,7 @@ "server": "Server", "serverVersion": "Server Version", "shrink": "Shrink", + "staticInitialize": "StaticInitialize", "toast.generateError": "Generation failed, {{error}}", "toast.generateSuccess": "Generation successful", "urlPattern": "URL Pattern", diff --git a/web/src/i18n/common/zh-CN.json b/web/src/i18n/common/zh-CN.json index 934c2023..0ff9c890 100644 --- a/web/src/i18n/common/zh-CN.json +++ b/web/src/i18n/common/zh-CN.json @@ -28,6 +28,7 @@ "server": "服务类型", "serverVersion": "服务版本", "shrink": "缩小字节码", + "staticInitialize": "静态初始化", "toast.generateError": "生成失败,{{error}}", "toast.generateSuccess": "生成成功", "urlPattern": "请求路径", diff --git a/web/src/pages/memshell.tsx b/web/src/pages/memshell.tsx index 5178ab4f..d15dfd7c 100644 --- a/web/src/pages/memshell.tsx +++ b/web/src/pages/memshell.tsx @@ -77,6 +77,7 @@ export default function MemShellPage() { injectorClassName: urlParams.injectorClassName ?? "", packingMethod: urlParams.packingMethod ?? "", shrink: urlParams.shrink ?? true, + staticInitialize: true, shellClassBase64: urlParams.shellClassBase64 ?? "", }, }); diff --git a/web/src/pages/probeshell.tsx b/web/src/pages/probeshell.tsx index d001d2ba..98dc8c3d 100644 --- a/web/src/pages/probeshell.tsx +++ b/web/src/pages/probeshell.tsx @@ -57,6 +57,7 @@ export default function ProbeShellGenerator() { seconds: 5, sleepServer: "Tomcat", shrink: true, + staticInitialize: true }, }); diff --git a/web/src/types/memshell.ts b/web/src/types/memshell.ts index a0dc5bfc..00349497 100644 --- a/web/src/types/memshell.ts +++ b/web/src/types/memshell.ts @@ -67,6 +67,7 @@ export interface InjectorConfig { injectorClassName?: string; classInheritance?: string; urlPattern?: string; + staticInitialize?: boolean; } export interface ConfigResponseType { diff --git a/web/src/types/probeshell.ts b/web/src/types/probeshell.ts index 850eedbd..94a37f5b 100644 --- a/web/src/types/probeshell.ts +++ b/web/src/types/probeshell.ts @@ -16,6 +16,7 @@ export interface ProbeConfig { debug?: boolean; byPassJavaModule?: boolean; shrink?: boolean; + staticInitialize?: boolean; } export interface ProbeContentConfig { diff --git a/web/src/types/schema.ts b/web/src/types/schema.ts index c15d7771..dca9c3d7 100644 --- a/web/src/types/schema.ts +++ b/web/src/types/schema.ts @@ -10,6 +10,7 @@ export const memShellFormSchema = yup.object({ targetJdkVersion: yup.string().optional(), debug: yup.boolean().optional(), byPassJavaModule: yup.boolean().optional(), + staticInitialize: yup.boolean().optional(), shellClassName: yup.string().optional(), shellTool: yup.string().required().min(1), shellType: yup.string().required().min(1), @@ -152,6 +153,7 @@ export const probeShellFormSchema = yup.object().shape({ debug: yup.boolean().optional(), byPassJavaModule: yup.boolean().optional(), shrink: yup.boolean().optional(), + staticInitialize: yup.boolean().optional() }); type ProbeValidationResult = ResolverResult; diff --git a/web/src/utils/transformer.ts b/web/src/utils/transformer.ts index b22b0170..817cc7bb 100644 --- a/web/src/utils/transformer.ts +++ b/web/src/utils/transformer.ts @@ -37,6 +37,7 @@ export function transformToPostData(formValue: MemShellFormSchema) { const injectorConfig: InjectorConfig = { urlPattern: formValue.urlPattern, injectorClassName: formValue.injectorClassName, + staticInitialize: formValue.staticInitialize }; return { shellConfig, @@ -54,6 +55,7 @@ export function transformToProbePostData(formValue: ProbeShellFormSchema) { shrink: formValue.shrink, debug: formValue.debug, byPassJavaModule: formValue.byPassJavaModule, + staticInitialize: formValue.staticInitialize }; const probeContentConfig: ProbeContentConfig = { host: formValue.host,