我正在尝试通过使用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);
}
}
由
执行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();
}
这看起来很简单:我在ACC_NATIVE
中删除visitMethod()
方法并保留其他所有内容。但是,当我对java.lang.Object
执行此操作时,它会以
Exception in thread "main"
Exception: java.lang.StackOverflowError thrown from the UncaughtExceptionHandler in thread "main"
StackOverflow发生在 instrumentation 时间,而不是运行时,我觉得这很不寻常。但是,如果我删除了& ~Opcodes.ACC_NATIVE
修饰符,java.lang.Object
会被重写(在这种情况下保持不变)并完美执行。
显然我没有做正确的事情,用非native
方法替换native
方法并不像在方法上剥离native
修饰符那么简单,但我不知道从哪里开始。 ASM Docs根本没有谈到使用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)
所以在我看来,错误实际上是在执行时发生的(例如,我认为它是在检测时错误)并且是调用hashCode()
的结果。正如它所发生的那样,hashCode()
我(可能错误地)剥离了它的native
修饰符的本地方法之一。很明显,它是调用 native
- 剥离的方法导致问题。
似乎真正奇怪的是,堆栈跟踪只有16帧深;考虑到它是StackOverflowError
,我会更加期待。
答案 0 :(得分:2)
使用存根替换本机代码并不是那么简单,但距离
如果你看ClassVisitor#visitMethod(int access, String name, String desc, String signature, String[] exceptions)
你会看到它返回MethodVisitor
MethodVisitor。如果要创建抽象存根,则应至少添加对methodVisitor.visitEnd()
的调用
如果您想制作空的存根,则必须添加visitCode
并在必要时返回值
答案 1 :(得分:1)
这里详细说明接受的答案是一个完整工作的示例,它使用ASM将本机方法java.net.NetworkInterface#getHardwareAddress()
替换为返回固定值的存根。
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();
}
}
}