fix: custom shell with jsr/ret generate failed

This commit is contained in:
ReaJason
2026-08-30 21:19:42 +08:00
parent 8514f1ae1f
commit 493e540984
4 changed files with 863 additions and 2 deletions
@@ -23,6 +23,10 @@ public abstract class ByteBuddyShellGenerator<T extends ShellToolConfig> impleme
protected abstract DynamicType.Builder<?> getBuilder();
protected int getTargetJreVersion() {
return shellConfig.getTargetJreVersion();
}
protected byte[] postProcessBytes(byte[] classBytes) {
return classBytes;
}
@@ -43,7 +47,7 @@ public abstract class ByteBuddyShellGenerator<T extends ShellToolConfig> impleme
builder = ProcessorRegistry.applyBuilderProcessors(builder, shellConfig, shellToolConfig)
.name(shellClassName)
.visit(new TargetJreVersionVisitorWrapper(shellConfig.getTargetJreVersion()));
.visit(new TargetJreVersionVisitorWrapper(getTargetJreVersion()));
try (DynamicType.Unloaded<?> unloaded = builder.make()) {
byte[] bytes = postProcessBytes(unloaded.getBytes());
@@ -7,6 +7,10 @@ import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.ClassFileLocator;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.jar.asm.ClassReader;
import net.bytebuddy.jar.asm.ClassVisitor;
import net.bytebuddy.jar.asm.Label;
import net.bytebuddy.jar.asm.MethodVisitor;
import net.bytebuddy.jar.asm.Opcodes;
import net.bytebuddy.pool.TypePool;
import org.apache.commons.lang3.StringUtils;
@@ -17,6 +21,7 @@ import java.util.Base64;
* @since 2025/3/18
*/
public class CustomShellGenerator extends ByteBuddyShellGenerator<CustomConfig> {
private boolean containsSubroutines;
public CustomShellGenerator(ShellConfig shellConfig, CustomConfig customConfig) {
super(shellConfig, customConfig);
@@ -27,6 +32,7 @@ public class CustomShellGenerator extends ByteBuddyShellGenerator<CustomConfig>
String shellClassBase64 = shellToolConfig.getShellClassBase64();
byte[] classBytes = Base64.getDecoder().decode(shellClassBase64);
ClassReader classReader = new ClassReader(classBytes);
containsSubroutines = containsSubroutines(classReader);
String className = classReader.getClassName().replace('/', '.');
if (StringUtils.isBlank(shellToolConfig.getShellClassName())) {
shellToolConfig.setShellClassName(className);
@@ -43,4 +49,38 @@ public class CustomShellGenerator extends ByteBuddyShellGenerator<CustomConfig>
return new ByteBuddy()
.redefine(typeDescription, compoundLocator);
}
@Override
protected int getTargetJreVersion() {
// Byte Buddy cannot emit a class version newer than Java 5 when the
// source bytecode contains legacy jsr/ret subroutines. Keep such
// custom classes at Java 5; Java 6+ runtimes can load them as well.
return containsSubroutines ? Opcodes.V1_5 : super.getTargetJreVersion();
}
private static boolean containsSubroutines(ClassReader classReader) {
final boolean[] found = {false};
classReader.accept(new ClassVisitor(Opcodes.ASM9) {
@Override
public MethodVisitor visitMethod(int access, String name, String descriptor,
String signature, String[] exceptions) {
return new MethodVisitor(Opcodes.ASM9) {
@Override
public void visitJumpInsn(int opcode, Label label) {
if (opcode == Opcodes.JSR) {
found[0] = true;
}
}
@Override
public void visitVarInsn(int opcode, int var) {
if (opcode == Opcodes.RET) {
found[0] = true;
}
}
};
}
}, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
return found[0];
}
}