【问题标题】:How to check that bytecode operation PUTFIELD is reassigning a field belonging to 'this' object using ObjectWeb ASM?如何检查字节码操作 PUTFIELD 是否正在使用 ObjectWeb ASM 重新分配属于“this”对象的字段?
【发布时间】:2010-08-28 15:58:40
【问题描述】:

我正在使用ASM 字节码操作框架对Java 代码执行静态分析。我希望检测何时重新分配对象的字段,即何时发生这种代码:

class MyObject {
    private int value;
    void setValue(int newValue) { this.value = newValue; }
}

使用以下代码(在实现ClassVisitor的类中)可以检测到上述情况:

@Override
public void visitFieldInsn(int opcode, String owner, String name, String desc) {
    if(opcode == Opcodes.PUTFIELD) {
        // do whatever here
    }
}

但是,无论拥有该字段的对象如何,都会调用此代码。我想找到更具体的情况,在 this 对象上执行 PUTFIELD 操作。比如我想区分第一个代码sn -p,和这样的代码:

public MyObject createNewObjectWithDifferentField() {
    MyObject newObject = new MyObject();
    newObject.value = 43;
    return newObject;
}

在上述情况下,仍然执行 PUTFIELD 操作,但这里它是在一个局部变量 (newObject) 上而不是 this 对象上。这将取决于分配时堆栈的状态,但我遇到了一些字节码完全不同的不同场景,我正在寻找处理这种复杂性的方法。

如何检查 PUTFIELD 是否正在重新分配属于 this 对象的字段?


编辑

我使用 ASM 仅执行分析,而不是检测现有字节码。如果可能的话,最好我想找到一种在不改变字节码的情况下发现这一点的方法。

【问题讨论】:

  • 在没有内部类的非混淆标准 Java 代码中,this.x 将始终编译为 aload_0 ... putfield。您甚至不需要跟踪变量。如果您担心任意代码,那么这是一个无法确定的问题。

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


【解决方案1】:

我认为一般情况下这是不可能的。考虑:

class MyObject {
  private int value;
  void mymethod1() {
    mymethod2(Math.random() > 0.5 ? this : new MyObject());
  }

  void mymethod2(MyObject that) {
    that.value = 1;
  }
}

在更简单的情况下,您可以将堆栈跟踪回ALOAD 0,在实例方法中它引用this

【讨论】:

  • 我认为你是对的,可能总会有办法欺骗我的分析,但代码越深奥,我就越不担心 :) 在跟踪方面ALOAD_0this 引用 保证 是否位于局部变量表的零位?我的印象是通常是这样,但并非总是如此......
  • 是的,它保证实例方法和构造函数。检查 JVMS 部分 4.9.4
  • ASM 包含一个验证分析器,您应该能够使用它来将堆栈回溯到 ALOAD_0。这就是一种方法中的全部内容。在不同的方法之间,情况就不同了。
【解决方案2】:

我从未使用过 ASM,但是,我有处理字节码的经验。

就在 PUTFIELD 指令之前,堆栈如下所示:

|...,object_ref,value

|...,object_ref,value1,value2(如果字段的类型是double或long)

以第一种情况为例,可以在 PUTFIELD 前插入如下指令:

1: DUP2
2: POP
3: ALOAD_0
4: IF_ACMPNE X
5: put your code here
...
...
X: PUTFIELD

指令 (1) 复制 object_ref 和堆栈上的值。 (2) 删除值。 (3) 加载“this”引用。 (4) 如果 'this' 等于 object_ref 执行你的代码,否则什么都不做并跳转到 PUTFIELD。

对于第二种情况(长字段或双字段),您可以使用这一系列字节码指令

1: DUP2_X1
2: POP2
3: DUP
4: ALOAD_0
5: IF_ACMPNE 7
6: put your code here
...
...
7: DUP_X2
8: POP
9: PUTFIELD

【讨论】:

  • 感谢您对堆栈状态的深入了解。但是,我使用 ASM 执行静态分析,您的解决方案涉及操作字节码(我不想这样做)和执行代码(我绝对不能这样做)。不过谢谢,特别是指出堆栈中的 longs、double 与其他数据类型的区别。
  • 是的,我错过了静态分析部分。我的大部分工作都是关于动态分析,所以这个解决方案突然出现在我的脑海中。在这种情况下,我同意 Jevgeni 的回答。
【解决方案3】:

另一种方法(运行时):

您可以使用 AspectJ 并为您的类设置字段集/获取切入点。请参阅:http://www.eclipse.org/aspectj/doc/released/progguide/semantics-pointcuts.htmlhttp://www.eclipse.org/aspectj/

定义切入点后,您将编写一些建议,通过使用 thisJoinPoint 变量简单地打印出当前执行位置。然后,当你运行你的程序时,你会得到一个很好的日志,记录字段被获取/设置的所有地方。

这将需要运行时或编译时编织,这意味着无论哪种方式都需要字节码操作。希望这会有所帮助...

【讨论】:

  • 不幸的是,由于我正在静态分析代码,而不执行它,这种方法将不起作用。无论如何谢谢:)
  • 啊,好吧,祝您使用 ASM 方法好运。您有源代码还是正在分析二进制文件?
猜你喜欢
  • 2014-07-24
  • 2021-07-17
  • 1970-01-01
  • 1970-01-01
  • 2020-10-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-04
相关资源
最近更新 更多