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

Jos*_*one 3 java bytecode-manipulation java-bytecode-asm

如何检查类的字节码(使用诸如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) {
}
Run Code Online (Sandbox Code Playgroud)

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

{
  Object foo = "foo";
  m1(foo);
  m2("bar");
  m3("baz");
}
Run Code Online (Sandbox Code Playgroud)

我怎么可以检查类的字节码得知m3将与值被称为4倍"foo","box","bar""baz"

Raf*_*ter 5

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

visitMethodIns(<any>, "bar.Foo", <any>, <any>)
Run Code Online (Sandbox Code Playgroud)

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

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

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

ALOAD_0 / ASTORE_1 / ALOAD_1 => ALOAD_0
Run Code Online (Sandbox Code Playgroud)

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

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

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]
Run Code Online (Sandbox Code Playgroud)

然后,您可以使用这些结果来解析块,在那里您可以找到这些方法调用及其含义.然而,你会创建一个非常脆弱的解决方案,其中的间接如下:

{
  Foo foo = this;
  foo.m1("bar");
}
Run Code Online (Sandbox Code Playgroud)

不会被发现.正如评论中所指出的,您基本上需要模拟Java虚拟机以"运行"您的代码.

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

  • 嗨@JoshStone,你得到了调用方法参数吗?如果是,请您分享有关其解决方案的信息?我也面临同样的问题.我得到调用方法的参数类型但是我需要传递给被调用方法的值. (2认同)