【问题标题】:Calling method of object after every putfield operations using bytecode manipulation library ASM使用字节码操作库 ASM 在每次 putfield 操作后调用对象的方法
【发布时间】:2014-07-24 11:34:04
【问题描述】:

我正在对一个 Java 客户端应用程序进行逆向工程,我想对其某些字段的修改进行跟踪,以查看在解决混淆名称的操作之后发生了什么变化。

我可能会使用某种调试器,但我决定自制它以获取学习经验。

我使用 ASM 创建了一个类适配器,它查看每个方法的每条指令,当它遇到 PUTFIELD 指令时,它应该调用对象的 fireCallback 方法

只要我可以期望 PUTFIELD 目标是工作类,这非常简单,但是如果它被其他对象调用,我无法弄清楚如何检测它调用的是哪个对象

我正在阅读 JVM 规范,我发现有一个 DUP 和 POP 指令可用于堆栈操作,当调用 PUTFIELD 时,堆栈上有对象引用和设置值,所以我想为什么我不能复制putfield 之前的堆栈,然后简单地弹出设置值并在剩余的 objectref 上调用 fireCallback

但这不可能那么简单,不幸的是,如果设置值是 double 或 long 类型,则 PUTFIELD 需要两个字,我无法简单地弄清楚如何处理这个异常?

或者有更简单的方法吗?如何知道 putfield 之前加载到堆栈的对象是什么?

我当前的代码:

    for (MethodNode method : (List<MethodNode>) methods) {

        if (ADD_FIELD_CALLBACKS) {
            Iterator<AbstractInsnNode> insIt = method.instructions
                    .iterator();

            while (insIt.hasNext()) {
                AbstractInsnNode ins = insIt.next();

                int opcode = ins.getOpcode();
                if (ins.getOpcode() == Opcodes.PUTFIELD) {
                    FieldInsnNode fieldInsNode = (FieldInsnNode) ins;

                    System.out.println(name + "'s updating "
                            + fieldInsNode.owner + "'s "
                            + fieldInsNode.name + " in method "
                            + method.name);

                    InsnList onFieldEditInsList = new InsnList();

                    method.instructions.insertBefore(ins, new InsnNode(
                            Opcodes.DUP2));

                    onFieldEditInsList.add(new InsnNode(Opcodes.POP));
                    onFieldEditInsList.add(new LdcInsnNode(
                            fieldInsNode.name));
                    onFieldEditInsList
                            .add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL,
                                    fieldInsNode.owner, "fireCallback",
                                    "(Ljava/lang/String;)V", false));

                    method.maxStack += 2;

                    method.instructions.insert(ins, onFieldEditInsList);
                }

            }
        }
    }

如果当时堆栈中存在不同的项目集,这会导致带有消息“期望在堆栈上找到对象/数组”的 VerifyError..

【问题讨论】:

  • 你不能只使用dup2,然后用Ljava.lang.Object 调用你的fireCallback 方法,然后使用Object.toString() 方法吗?它至少可以帮助调试
  • 我不明白这将如何解决问题?我的意思是,代码甚至没有运行,因为它没有通过验证..

标签: java reverse-engineering instrumentation java-bytecode-asm bytecode-manipulation


【解决方案1】:

您的问题看起来有点奇怪,因为putfield 指令已经包含了您需要的所有信息。它有一个reference to the owner class 和一个field signature telling the type of the field

因此,对于遇到putfield 指令时的有效字节码,栈顶的值必须与字段的类型兼容,并且下一个值必须是对与所有者类兼容的实例的引用。换句话说,如果该字段的签名是"J""D",则您有一个值占用堆栈顶部的两个字,否则它是一个字。

请注意,对于此类任务,使用GeneratorAdapter 更方便。当用作访问者时,默认情况下会复制遇到的代码,并且可以计算 maxStack 和 maxLocal 值等。因此,您只需覆盖要注入代码的指令的访问方法,而无需实现迭代逻辑。

例如对于每个putField 指令,在mypackage.MyDebugger 类中调用类似public static void fieldWrite(Object owner, String fieldName) 的方法,代码如下所示:

// i.e. class MyCodeTransformer extends GeneratorAdapter
@Override
public void visitFieldInsn(int opcode, String owner, String name, String desc) {
  if(opcode==Opcodes.PUTFIELD) {
    final Type fieldType = Type.getType(desc);
    super.swap(Type.getObjectType(owner), fieldType);
    if(fieldType.getSize()==1) super.dupX1(); else super.dupX2();
    super.visitLdcInsn(name);
    super.visitMethodInsn(Opcodes.INVOKESTATIC, "mypackage/MyDebugger",
        "fieldWrite", "(Ljava/lang/Object;Ljava/lang/String;)V", false);
  }
  super.visitFieldInsn(opcode, owner, name, desc);
}

【讨论】:

  • 非常感谢您的友好回答!很抱歉我的问题看起来很奇怪,但有时很难在注意到之前看到明显的事情..
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-10-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多