tag standard library lib funciones curso java java-native-interface metaprogramming sandbox bytecode

standard - Reescritura de métodos nativos de Java utilizando ASM



tag lib jstl (2)

Para elaborar más detalladamente la respuesta aceptada, aquí hay un ejemplo completo de un agente de instrumentación que utiliza ASM para reemplazar el método nativo java.net.NetworkInterface#getHardwareAddress() con un código auxiliar que devuelve un valor fijo.

public class MacModifyAgent { private static final String TARGET = "java/net/NetworkInterface"; public static void premain(String agentArgs, Instrumentation inst) { inst.addTransformer(new ClassFileTransformer() { @Override public byte[] transform(ClassLoader l, String name, Class<?> c, ProtectionDomain d, byte[] b) throws IllegalClassFormatException { if (TARGET.equals(name)) { return instrument(b); } return b; } }); } private static byte[] instrument(byte[] originalBytes) { 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(); } public static class ClassAdapter extends ClassVisitor implements Opcodes { public ClassAdapter(ClassVisitor cv) { super(ASM4, cv); } @Override public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { if ("getHardwareAddress".equals(name)) { MethodVisitor mv = super.visitMethod(access & ~ACC_NATIVE, name, descriptor, signature, exceptions); MethodVisitor special = new StubReturnValue(mv, new byte[] { 1, 2, 3, 4, 5, 6 }); return special; } else { return super.visitMethod(access, name, descriptor, signature, exceptions); } } } public static class StubReturnValue extends MethodVisitor implements Opcodes { private final MethodVisitor target; private byte[] mac; public StubReturnValue(MethodVisitor target, byte [] mac) { super(ASM4, null); this.target = target; } @Override public void visitCode() { target.visitCode(); target.visitVarInsn(BIPUSH, 6); target.visitIntInsn(NEWARRAY, T_BYTE); for (int i = 0; i < 6; i++) { target.visitInsn(DUP); target.visitIntInsn(BIPUSH, i); target.visitIntInsn(BIPUSH, mac[i]); target.visitInsn(BASTORE); } target.visitInsn(ARETURN); target.visitEnd(); } } }

Intento hacer esto reescribiendo el código de bytes de la clase utilizando ASM 4.0 para reemplazar todos los métodos native con apéndices no native .

Hasta ahora tengo esto:

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); } }

que es ejecutado por

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(); }

Lo que parece bastante simple: ACC_NATIVE el ACC_NATIVE del método en visitMethod() y dejo todo lo demás sin cambios. Sin embargo, cuando hago esto a java.lang.Object , muere con un

Exception in thread "main" Exception: java.lang.StackOverflowError thrown from the UncaughtExceptionHandler in thread "main"

El StackOverflow ocurre en el momento de la instrumentación , no en el tiempo de ejecución , lo que creo que es bastante inusual. Sin embargo, si & ~Opcodes.ACC_NATIVE modificador & ~Opcodes.ACC_NATIVE , java.lang.Object vuelve a escribir (en este caso, sin cambios) y se ejecuta perfectamente.

Claramente, no estoy haciendo algo bien, y reemplazar el método native con un método no native no es tan simple como eliminar el modificador native del método, pero no tengo idea de por dónde empezar. Los documentos de ASM no hablan de trabajar con métodos native en absoluto. ¿Alguien con experiencia en el trabajo con ASM sabe lo que debo hacer para que el método native se vuelva a escribir?

EDITAR

Lo siento, ese mensaje corto e inútil fue lo que me dio e.printStackTrace() , pero al usar e.getStackTrace() logré obtener algo útil:

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)

Por lo tanto, me parece que el error ocurrió en el momento de la ejecución (por ejemplo, me equivoqué al pensar que fue en el momento de la instrumentación) y es el resultado de llamar a hashCode() . Como sucede, hashCode() uno de los métodos nativos que (de manera incorrecta) eliminé de su modificador native . Así que claramente está llamando a los métodos native eliminados que está causando el problema.

Lo que parece realmente extraño es que la traza de la pila tiene solo 16 cuadros de profundidad; Habría esperado un poco más dado que era un StackOverflowError .