Li *_*oyi 17 java java-native-interface bytecode metaprogramming sandbox
我试图通过使用ASM 4.0重写类的字节码来替换所有native非native存根方法.
到目前为止我有这个:
class ClassAdapter extends ClassVisitor {
public ClassAdapter(ClassVisitor cv) {
super(Opcodes.ASM4, cv);
}
@Override
public MethodVisitor visitMethod(int access, String base, String desc, String signature, String[] exceptions) {
return cv.visitMethod(access & ~Opcodes.ACC_NATIVE, base, desc, signature, exceptions);
}
}
Run Code Online (Sandbox Code Playgroud)
由执行
private static byte[] instrument(byte[] originalBytes, ClassLoader loader) {
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
ClassAdapter adapter = new ClassAdapter(cw);
ClassReader cr = new ClassReader(originalBytes);
cr.accept(adapter, ClassReader.SKIP_FRAMES);
return cw.toByteArray();
}
Run Code Online (Sandbox Code Playgroud)
这似乎很简单:我删除ACC_NATIVE了方法visitMethod()并保留其他所有内容.然而,当我这样做时java.lang.Object,它会死于
Exception in thread "main"
Exception: java.lang.StackOverflowError thrown from the UncaughtExceptionHandler in thread "main"
Run Code Online (Sandbox Code Playgroud)
StackOverflow发生在检测时,而不是在运行时,我觉得这很不寻常.但是,如果我删除& ~Opcodes.ACC_NATIVE修饰符,则会java.lang.Object重写(在这种情况下保持不变)并完美执行.
很明显,我没有做正确的事情,用native非native方法替换方法并不像native在方法上剥离修饰符那么简单,但我不知道从哪里开始.该ASM文档不谈论与工作native方法的.有没有使用ASM经验的人知道我需要做什么才能让native方法重写工作吗?
对不起,这条简短无用的消息是e.printStackTrace()给我的,但是使用e.getStackTrace()我设法获得了一些有用的东西:
java.util.concurrent.ConcurrentHashMap.hash(ConcurrentHashMap.java:332)
java.util.concurrent.ConcurrentHashMap.put(ConcurrentHashMap.java:1124)
java.util.Collections$SetFromMap.add(Collections.java:3903)
sandbox.classloader.MyClassLoader.instrument(Unknown Source)
sandbox.classloader.MyClassLoader.loadClass(Unknown Source)
java.lang.ClassLoader.defineClass1(Native Method)
java.lang.ClassLoader.defineClass(ClassLoader.java:791)
java.lang.ClassLoader.defineClass(ClassLoader.java:634)
sandbox.classloader.MyClassLoader.findClass(Unknown Source)
sandbox.classloader.MyClassLoader.loadClass(Unknown Source)
sandbox.Tester.main(Unknown Source)
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
java.lang.reflect.Method.invoke(Method.java:601)
com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)
Run Code Online (Sandbox Code Playgroud)
所以在我看来,错误实际上是在执行时发生的(例如,我认为它是在仪表时间错误)并且是调用的结果hashCode().正如它所发生的那样,hashCode()我(可能错误地)剥离了它的native修饰符之一的本机方法.很明显,它正在调用native导致问题的-stripped方法.
看起来很奇怪的是,堆栈跟踪只有16帧深; 鉴于它是一个,我会更加期待StackOverflowError.
用存根替换本机代码并不那么简单,但也离那不远了
如果你看一下ClassVisitor#visitMethod(int access, String name, String desc, Stringsignature, String[] excepts)
你会发现它返回一个MethodVisitor
您现在必须使用MethodVisitor 。如果你想制作抽象存根,你应该至少添加对methodVisitor.visitEnd()
如果您想创建空存根,则必须添加visitCode并在必要时返回一个值