有没有办法打印调用实例/静态方法的对象引用,使用字节码检测

时间:2016-02-23 07:46:33

标签: java-bytecode-asm

我读到某个地方,当“invokevirtual”调用一个方法时, 从堆栈顶部获取对象引用,后跟参数。 我需要以某种方式打印对象引用。有可能吗?

1 个答案:

答案 0 :(得分:2)

所以,我不打算为你做这件事,因为实际的代码很烦人而且乏味,如果你真的真的感兴趣,你应该自己学习如何做。但我会尽力提供帮助并为您提供一些指导。

首先,您要阅读ASM tutorials here

我将在下面写的字节代码格式来自ASMIfier,因为它更加清晰。我会完全忽略javap,因为它更加迂腐和详细,但如果你想知道它实际上在向你显示什么,那么你应该阅读Java ClassFile format

实际上,无论如何,你应该首先做到这一点,只是为了确保你的背景知识有所填写。

所以,这就是你想要做的事情的简要介绍。您将要编写一个查找INVOKEVIRTUAL操作码实例的ClassWriter

invokevirtual以相反的顺序弹出堆栈中的值,因此最后一个参数和您最后一次调用的对象。你引用的#38也不是对象,它是对常量池的引用,它包含一个方法名称和方法描述符对,它用作元数据,因为JVM是类型安全的。

让我们假设你有这个代码:

包装样品;

public class JavaSimpleHelloWorld {
    public static void main(String[] args) {

        System.out.println("Hello World");
    }
}

如果您针对它运行ASMIFier,那么您只会为主要方法获取类似内容(为了简洁而缩减上下文)

  public static main([Ljava/lang/String;)V
   L0
    LINENUMBER 6 L0
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
    LDC "Hello World"
    INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
   L1
    LINENUMBER 7 L1
    RETURN
   L2
    LOCALVARIABLE args [Ljava/lang/String; L0 L2 0
    MAXSTACK = 2
    MAXLOCALS = 1

所以,你实现了某种静态转储方法(public static final dump(Object o)),并编写了一个重组字节代码的类访问者。

您可以使用方法描述符来确定插入DUP / INVOKE以打印方法对象目标所需的前导堆栈推送指令(ALOAD,LDC)的深度。例如,System.out.println的方法描述符是[Ljava / lang / String;] V这意味着该方法接受一个字符串数组并返回void。因此,您需要在堆栈中返回1以找到对象目标。反过来,你的字节码看起来像这样:

快乐的字节码twiddling。

  public static main([Ljava/lang/String;)V
   L0
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
    DUP
    INVOKESTATIC my/staticutil/ClassThatDumps.dump (Ljava/lang/Object;)V
    LDC "Hello World"
    INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
    RETURN
   L1
    LOCALVARIABLE args [Ljava/lang/String; L0 L1 0
    MAXSTACK = 2
    MAXLOCALS = 1