feat: support scriptEngineJar packer

This commit is contained in:
ReaJason
2025-11-20 00:36:25 +08:00
parent a022968965
commit 5ee604f4a0
25 changed files with 389 additions and 82 deletions
@@ -0,0 +1,47 @@
package com.reajason.javaweb.asm;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Opcodes;
/**
* @author ReaJason
* @since 2025/11/16
*/
public class ClassInterfaceUtils {
public static byte[] addInterface(byte[] bytes, String interfaceName) {
ClassReader cr = new ClassReader(bytes);
ClassWriter cw = new ClassWriter(cr, 0);
ClassVisitor cv = new AddInterfaceClassAdapter(cw, interfaceName.replace('.', '/'));
cr.accept(cv, 0);
return cw.toByteArray();
}
static class AddInterfaceClassAdapter extends ClassVisitor {
private final String interfaceToAdd;
public AddInterfaceClassAdapter(ClassVisitor cv, String interfaceToAdd) {
super(Opcodes.ASM9, cv);
this.interfaceToAdd = interfaceToAdd;
}
@Override
public void visit(int version, int access, String name,
String signature, String superName, String[] interfaces) {
for (String itf : interfaces) {
if (itf.equals(interfaceToAdd)) {
super.visit(version, access, name, signature, superName, interfaces);
return;
}
}
String[] newInterfaces = new String[interfaces.length + 1];
System.arraycopy(interfaces, 0, newInterfaces, 0, interfaces.length);
newInterfaces[interfaces.length] = interfaceToAdd;
super.visit(version, access, name, signature, superName, newInterfaces);
}
}
}
@@ -0,0 +1,120 @@
package com.reajason.javaweb.asm;
import net.bytebuddy.ByteBuddy;
import org.junit.jupiter.api.Test;
import org.objectweb.asm.ClassReader;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineFactory;
import java.io.Serializable;
import java.util.Collections;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
/**
* @author ReaJason
* @since 2025/11/16
*/
class ClassInterfaceUtilsTest {
@Test
void test() {
String interfaceName = "javax.script.ScriptEngineFactory";
byte[] bytes = new ByteBuddy().redefine(EmptyInterface.class).make().getBytes();
byte[] newBytes = ClassInterfaceUtils.addInterface(bytes, interfaceName);
String[] interfaces = new ClassReader(newBytes).getInterfaces();
assertEquals(1, interfaces.length);
assertEquals(interfaceName.replace(".", "/"), interfaces[0]);
}
@Test
void skipAdd(){
String interfaceName = "javax.script.ScriptEngineFactory";
byte[] bytes = new ByteBuddy().redefine(ScriptEngineFactoryClass.class).make().getBytes();
byte[] newBytes = ClassInterfaceUtils.addInterface(bytes, interfaceName);
String[] interfaces = new ClassReader(newBytes).getInterfaces();
assertEquals(1, interfaces.length);
assertEquals(interfaceName.replace(".", "/"), interfaces[0]);
}
@Test
void addNewInterface(){
String interfaceName = "javax.script.ScriptEngineFactory";
byte[] bytes = new ByteBuddy().redefine(Entity.class).make().getBytes();
byte[] newBytes = ClassInterfaceUtils.addInterface(bytes, interfaceName);
String[] interfaces = new ClassReader(newBytes).getInterfaces();
assertEquals(2, interfaces.length);
assertEquals("java/io/Serializable", interfaces[0]);
assertEquals(interfaceName.replace(".", "/"), interfaces[1]);
}
static class EmptyInterface {
}
static class ScriptEngineFactoryClass implements ScriptEngineFactory {
@Override
public String getEngineName() {
return "";
}
@Override
public String getEngineVersion() {
return "";
}
@Override
public List<String> getExtensions() {
return Collections.emptyList();
}
@Override
public List<String> getMimeTypes() {
return Collections.emptyList();
}
@Override
public List<String> getNames() {
return Collections.emptyList();
}
@Override
public String getLanguageName() {
return "";
}
@Override
public String getLanguageVersion() {
return "";
}
@Override
public Object getParameter(String key) {
return null;
}
@Override
public String getMethodCallSyntax(String obj, String m, String... args) {
return "";
}
@Override
public String getOutputStatement(String toDisplay) {
return "";
}
@Override
public String getProgram(String... statements) {
return "";
}
@Override
public ScriptEngine getScriptEngine() {
return null;
}
}
static class Entity implements Serializable {
}
}