扩展 class(例如枚举)的匿名 .class 能否以实现接口的方式被黑客入侵?
Can an anonymous .class that extends a class (such as an enum) be hacked in such a way as to implement an interface?
我有这样的界面:
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。)
(这与 "What can do you in Java bytecode that you can't do in Java?" 个问题有关。)
确实可以通过字节码操作创建这样的 class 文件。这是一个使用 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.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
当 运行ning 在 Oracles 的 Java 8 下时 class 文件驻留在文件系统中。当 运行ning 在不同的 JVM 下或捆绑在 Jar 文件中时,写回修改后的 class 文件可能会失败,或者 enum
可能已经被急切地加载并且没有反映修改(但它会下一个 运行 then).
但它表明,通常情况下,字节码操作是可能的,并且在启动应用程序之前执行时,即使在更复杂的情况下也能正常工作。
不过,我认为这只是一个有趣的 hack,但您不应该在其上构建应用程序。没有问题,这个 hack 将解决你无法使用实现接口的委托对象解决的问题......
我有这样的界面:
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。)
(这与 "What can do you in Java bytecode that you can't do in Java?" 个问题有关。)
确实可以通过字节码操作创建这样的 class 文件。这是一个使用 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.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
当 运行ning 在 Oracles 的 Java 8 下时 class 文件驻留在文件系统中。当 运行ning 在不同的 JVM 下或捆绑在 Jar 文件中时,写回修改后的 class 文件可能会失败,或者 enum
可能已经被急切地加载并且没有反映修改(但它会下一个 运行 then).
但它表明,通常情况下,字节码操作是可能的,并且在启动应用程序之前执行时,即使在更复杂的情况下也能正常工作。
不过,我认为这只是一个有趣的 hack,但您不应该在其上构建应用程序。没有问题,这个 hack 将解决你无法使用实现接口的委托对象解决的问题......