使用ASM跟踪字节码中的方法调用参数

时间:2014-05-29 01:01:12

标签: java java-bytecode-asm bytecode-manipulation

如何检查类的字节码(使用ASM之类的东西)来了解哪些初始值传递给方法?

例如:给定一些将值互相传递的方法:

void m1(Object o) {
  Object v = o;
  m2(v);
  m2("box");
}

void m2(Object o) {
  Object v = o;
  m3(x);
}  

void m3(Object o) {
}

一些方法调用,都在同一个类中定义:

{
  Object foo = "foo";
  m1(foo);
  m2("bar");
  m3("baz");
}

我如何检查班级'字节码,了解m3将使用值"foo""box""bar""baz"进行4次调用?

1 个答案:

答案 0 :(得分:5)

使用ASM,理论上可以跟踪每个方法,如果从其中调用同一个类的另一个方法。负责定义方法调用的访问者API方法是visitMethodIns。假设您的类名为bar.Foo,则需要跟踪:

visitMethodIns(<any>, "bar.Foo", <any>, <any>)

然后,您需要构建一个相互调用的方法的传递关系,其中最后两个参数允许您构建这样的关系层次结构。另外,您需要跟踪这些方法调用的参数,更棘手但也不是不可能。

它更复杂的原因是参数可以加载到操作数堆栈的可能方式的数量。对于您的示例,您只需要关注visitInsvisitLCDIns回调。

在常量池值(LCD)上调用方法时,参数的分辨率相当微不足道。但是,在调用方法学习局部变量赋值之前,您需要跟踪整个指令链,以便知道您正在调用方法参数上的方法。因此,你可以找到

ALOAD_0 / ASTORE_1 / ALOAD_1 => ALOAD_0

是从方法局部变量数组读取/写入序列的有效结果。

通过这一切,通过解析字节代码,您将了解以下调用转换:

m1(Ljava/lang/Object)V -> m2(Ljava/lang/Object)V [ALOAD 0]
                       -> m2(Ljava/lang/Object)V [LCD "box"] 
m2(Ljava/lang/Object)V -> m3(Ljava/lang/Object)V [ALOAD 0]

然后,您可以使用这些结果来解析您发现这些方法调用及其含义的块。然而,你会创建一个非常脆弱的解决方案,其中包括:

{
  Foo foo = this;
  foo.m1("bar");
}

不会被发现。正如评论中指出的那样,您基本上需要模拟Java虚拟机以便运行&#34;运行&#34;你的代码。

即使您实施了一个复杂的解决方案来追踪所有这些,您仍然无法确定您的结果。当我从实现中调用接口方法时会发生什么。还是一个子类的方法?由于动态调度方法,您永远无法确定被调用的目标。