扩展类(例如枚举)的匿名.class是否可以通过实现接口的方式进行黑客攻击?

时间:2015-08-13 00:30:24

标签: java jvm bytecode-manipulation

我有一个界面,如:

public interface Foo() {
    public void bar();
}

我想创建一个实现它的匿名枚举,就好像这是有效的Java:

public enum MyEnum {
    A implements Foo {
        public void bar() {
            System.out.println("Hello!");
        }
    },
    B,
    C;
}

(注意只有A实现Foo,而不是B或C.)

(这与那些" Java字节码中你可以用Java做什么?"问题。)

1 个答案:

答案 0 :(得分:2)

确实可以通过字节码操作创建这样的类文件。这是一个使用ASM库执行此操作的示例程序:

import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Paths;
import org.objectweb.asm.*;

public class EnumHack {
    interface Foo {
        public void bar();
    }
    enum MyEnum {
        A {
            public void bar() {
                System.out.println("Hello!");
            }
        },
        B,
        C;
    }
    public static void main(String... arg) {
        try {
            patch();
        } catch (IOException|URISyntaxException ex) {
            System.err.println("patching failed: "+ex);
            return;
        }
        test();
    }
    static void test() {
        for(MyEnum e: MyEnum.values()) {
            System.out.println(e.name());
            if (e instanceof Foo) {
                System.out.println("\timplements Foo");
                ((Foo)e).bar();
            }
        }
    }
    static void patch() throws IOException, URISyntaxException {
        URL url = MyEnum.class.getResource("EnumHack$MyEnum$1.class");
        ClassReader cr=new ClassReader(url.openStream());
        ClassWriter cw=new ClassWriter(cr, 0);
        cr.accept(new ClassVisitor(Opcodes.ASM5, cw) {
            @Override
            public void visit(int version, int access, String name,
                    String signature, String superName, String[] interfaces) {
                if(interfaces.length==0) // not patched yet
                    interfaces=new String[] { Foo.class.getName() };
                super.visit(version, access, name, signature, superName, interfaces);
            }
        }, 0);
        Files.write(Paths.get(url.toURI()), cw.toByteArray());
    }
}

打印

A
    implements Foo
Hello!
B
C

在Oracles的Java 8下运行时,类文件驻留在文件系统中。当在不同的JVM下运行或捆绑在Jar文件中时,回写修改后的类文件可能会失败,或者enum可能已经急切地加载而不反映修改(但它会在下次运行时)。

但它表明,通常情况下,字节码操作是可能的,并且在启动应用程序之前执行时,它甚至可以在更复杂的场景中工作。

尽管如此,我认为这只是一个有趣的有趣的黑客,但你不应该建立应用程序。没有问题,这个hack将解决你无法使用实现接口的委托对象解决...