如何在使用 ASM 进行字节码转换后定义 class(class 文件版本 0.0)
How define class after byte code transformation with ASM (class file version 0.0)
使用 ASM 库修改字节码后,我无法加载 class。
这是恒等变换器,我希望得到修改后的数组,其大小与第一个字节相同,但要短 2 倍! (439 对 278)
String path = SimpleClass.class.getName().replace(".", "/") + ".class";
ClassLoader classLoader = SimpleClass.class.getClassLoader();
InputStream is = classLoader.getResourceAsStream(path);
byte[] bytes = IOUtils.toByteArray(is);
ClassReader reader = new ClassReader(bytes);
ClassWriter writer = new ClassWriter(reader, 0);
byte[] modified = writer.toByteArray();
加载失败没什么了不起的。
我怀疑 header 被截断了,但第一个字节是相同的
两个数组。
-54, -2, -70
static class ByteClassLoader extends ClassLoader {
public Class define(String name, byte[] body) {
return defineClass(name, body, 0, body.length);
}
}
ByteClassLoader myLoader = new ByteClassLoader();
Class myClass = myLoader.define("Ooo", modified);
失败并出现错误:
java.lang.UnsupportedClassVersionError: Ooo has been compiled by a more recent version of the Java Runtime (class file version 0.0), this version of the Java Runtime only recognizes class file versions up to 52.0
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
at java.lang.ClassLoader.defineClass(ClassLoader.java:642)
at PrintTest$ByteClassLoader.define(PrintTest.java:24)
at PrintTest.x(PrintTest.java:48)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access[=14=]0(ParentRunner.java:58)
at org.junit.runners.ParentRunner.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:117)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:42)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:253)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:84)
这些字节对应于 0xcafeba
匹配 Java class 文件幻数,0xcafebabe
.
这里的问题是 new ClassWriter(reader, 0)
并没有像您认为的那样做;请参阅 API documentation:
classReader
- the ClassReader used to read the original class. It will be used to copy the entire constant pool from the original class and also to copy other fragments of original bytecode where applicable.
我们还需要让作者通过ClassReader.accept
访问reader,如下:
reader.accept(writer, 0);
附带说明一下,您不需要 IOUtils.toByteArary
,因为 ClassReader
有一个 constructor taking InputStream
。
使用 ASM 库修改字节码后,我无法加载 class。
这是恒等变换器,我希望得到修改后的数组,其大小与第一个字节相同,但要短 2 倍! (439 对 278)
String path = SimpleClass.class.getName().replace(".", "/") + ".class";
ClassLoader classLoader = SimpleClass.class.getClassLoader();
InputStream is = classLoader.getResourceAsStream(path);
byte[] bytes = IOUtils.toByteArray(is);
ClassReader reader = new ClassReader(bytes);
ClassWriter writer = new ClassWriter(reader, 0);
byte[] modified = writer.toByteArray();
加载失败没什么了不起的。 我怀疑 header 被截断了,但第一个字节是相同的 两个数组。
-54, -2, -70
static class ByteClassLoader extends ClassLoader {
public Class define(String name, byte[] body) {
return defineClass(name, body, 0, body.length);
}
}
ByteClassLoader myLoader = new ByteClassLoader();
Class myClass = myLoader.define("Ooo", modified);
失败并出现错误:
java.lang.UnsupportedClassVersionError: Ooo has been compiled by a more recent version of the Java Runtime (class file version 0.0), this version of the Java Runtime only recognizes class file versions up to 52.0
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
at java.lang.ClassLoader.defineClass(ClassLoader.java:642)
at PrintTest$ByteClassLoader.define(PrintTest.java:24)
at PrintTest.x(PrintTest.java:48)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access[=14=]0(ParentRunner.java:58)
at org.junit.runners.ParentRunner.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:117)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:42)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:253)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:84)
这些字节对应于 0xcafeba
匹配 Java class 文件幻数,0xcafebabe
.
这里的问题是 new ClassWriter(reader, 0)
并没有像您认为的那样做;请参阅 API documentation:
classReader
- the ClassReader used to read the original class. It will be used to copy the entire constant pool from the original class and also to copy other fragments of original bytecode where applicable.
我们还需要让作者通过ClassReader.accept
访问reader,如下:
reader.accept(writer, 0);
附带说明一下,您不需要 IOUtils.toByteArary
,因为 ClassReader
有一个 constructor taking InputStream
。