我正在对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);
                }

            }
        }
    }


如果此时堆栈中有不同的项目集,则会导致带有一条消息“ Expecting to find object / array on stack”的VerifyError。

最佳答案

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

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

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

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

// 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);
}

09-07 00:03