注入 try/catch 块以通过 ASM 对字节码进行序列化检查

如何解决注入 try/catch 块以通过 ASM 对字节码进行序列化检查

我是 ASM 的新手,我需要一些与字节码转换相关的帮助。

我想通过 ASM 为字节码中的每个局部变量添加带有 try/catch 块的打印函数。我发现之前关于添加 try/catch 块的问题是关于整个方法的。 我对堆栈映射框架知之甚少,因此将不胜感激任何指针。提前致谢。

我对每个对象的期望,例如someObject:如果这个对象是可序列化的,则打印它的序列化表示,如果不是,则使用 toString() 打印:

try {
  ByteArrayOutputStream bos = new ByteArrayOutputStream();
  ObjectOutputStream oos = new ObjectOutputStream(bos);
  oos.writeObject(someObject);
  String serializedobject = Base64.getEncoder().encodetoString(bos.toByteArray());
  oos.close();
  System.out.println(serializedobject);
} catch (IOException ex) {
  System.out.println(someObject.toString());
}

因为我试图对每个对象都这样做,所以我在 visitvarInsn() 中覆盖了 MethodVisitor,如下所示:

@Override
public void visitvarInsn(int opcode,int var) {
  super.visitvarInsn(opcode,var);
  switch (opcode) {
    case Opcodes.ASTORE:
      Label tryStart = new Label ();
      Label tryEnd = new Label ();
      Label catchStart = new Label ();
      Label catchEnd = new Label ();
      mv.visitTryCatchBlock(tryStart,tryEnd,catchStart,"java/io/IOException");

      mv.visitLabel(tryStart);
      // ==> ByteArrayOutputStream bos = new ByteArrayOutputStream();
      mv.visitTypeInsn(Opcodes.NEW,"java/io/ByteArrayOutputStream");
      mv.visitInsn(Opcodes.DUP);
      mv.visitMethodInsn(INVOKESPECIAL,"java/io/ByteArrayOutputStream","<init>","()V",false);
      mv.visitvarInsn(Opcodes.ASTORE,var + 1);
      // ==> ObjectOutputStream oos = new ObjectOutputStream(bos);
      mv.visitTypeInsn(Opcodes.NEW,"java/io/ObjectOutputStream");
      mv.visitInsn(Opcodes.DUP);
      mv.visitvarInsn(Opcodes.ALOAD,var + 1);
      mv.visitMethodInsn(INVOKESPECIAL,"java/io/ObjectOutputStream","(Ljava/io/OutputStream;)V",var + 2);
      // ==> oos.writeObject(someObject);
      mv.visitvarInsn(Opcodes.ALOAD,var + 2);
      mv.visitvarInsn(Opcodes.ALOAD,var);
      mv.visitMethodInsn(INVOKEVIRTUAL,"writeObject","(Ljava/lang/Object;)V",false);
      // ==> String serializedobject = Base64.getEncoder().encodetoString(bos.toByteArray());
      mv.visitMethodInsn(INVOKESTATIC,"java/util/Base64","getEncoder","()Ljava/util/Base64$Encoder;",false);
      mv.visitvarInsn(Opcodes.ALOAD,var + 1);
      mv.visitMethodInsn(INVOKEVIRTUAL,"toByteArray","()[B",false);
      mv.visitMethodInsn(INVOKEVIRTUAL,"java/util/Base64$Encoder","encodetoString","([B)Ljava/lang/String;",var + 3);
      // ==> oos.close();
      mv.visitvarInsn(Opcodes.ALOAD,var + 2);
      mv.visitMethodInsn(INVOKEVIRTUAL,"close",false);
      // ==> System.out.println
      mv.visitFieldInsn(Opcodes.GETSTATIC,"java/lang/System","out","Ljava/io/PrintStream;");
      mv.visitvarInsn(Opcodes.ALOAD,var + 3);
      mv.visitMethodInsn(INVOKEVIRTUAL,"java/io/PrintStream","println","(Ljava/lang/String;)V",false);

      mv.visitLabel(tryEnd);
      mv.visitJumpInsn(Opcodes.GOTO,catchEnd);

      mv.visitLabel(catchStart);
      mv.visitvarInsn(ASTORE,var + 1); // store exception
      // ==> System.out.println
      mv.visitFieldInsn(Opcodes.GETSTATIC,"java/lang/Object","toString","()Ljava/lang/String;",false);

      mv.visitLabel(catchEnd);

      // not sure whether I should add this.
      mv.visitLocalVariable("e","Ljava/io/IOException;",null,catchEnd,var + 1);
      break;
    default: // do nothing
  }
}

但是当我测试时,我一直收到 NotSerializableException -- 我以为我使用 try-catch 来捕获此异常。

我不确定是否应该为 try-catch 块添加 visitFrame(我也不知道该怎么做)。

PS -- 任何有关为每个局部变量进行日志记录的其他更好方法的指针也将受到高度赞赏!

解决方法

您构建 try-catch 块的逻辑是正确的,只是您使用的变量 var + 1var + 3 可能与原始代码的使用冲突。当我尝试使用您的代码来检测一个专门选择的示例以使其没有此类变量冲突时,它会起作用。

您可以使用 LocalVariablesSorter 解决此类问题,但它需要调用 newLocal 来为您注入的代码声明一个变量,并且由于您的代码中没有这样的调用,我假设您'不使用 LocalVariablesSorter

通常,注入如此复杂的代码,甚至可能多次注入,不仅容易出错,而且可能会显着增加代码大小,甚至超过方法的最大代码大小。

最好的方法是将复杂的代码单独移动到一个方法中,甚至可以以预编译的形式交付,即使用普通 Java 源代码创建,并且只注入对该方法的调用。

所以,假设有一个像

这样的助手类
package mypackage;

import java.io.*;
import java.util.Base64;

public class MyUtil {
    public static void printSerializedWithToStringFallback(Object someObject) {
        try {
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(someObject);
            oos.close();
            System.out.println(Base64.getEncoder().encodeToString(bos.toByteArray()));
          } catch(IOException ex) {
            System.out.println(someObject.toString());
          }
    }
}

你可以像这样注入调用

@Override
public void visitVarInsn(int opcode,int var) {
    super.visitVarInsn(opcode,var);
    if(opcode == Opcodes.ASTORE) {
        super.visitVarInsn(Opcodes.ALOAD,var);
        super.visitMethodInsn(Opcodes.INVOKESTATIC,"mypackage/MyUtil","printSerializedWithToStringFallback","(Ljava/lang/Object;)V",false);
    }
}

注入这个调用不会引入任何分支,所以不需要重新计算堆栈映射表。甚至对堆栈的要求也不会改变。注入的代码不会引入新的局部变量,其最高堆栈大小在 aload 之后,与 astore 之前的堆栈大小相同。所以这个简单的检测不需要 COMPUTE_FRAMES 选项,甚至不需要 COMPUTE_MAXS

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?
Java在半透明框架/面板/组件上重新绘画。
Java“ Class.forName()”和“ Class.forName()。newInstance()”之间有什么区别?
在此环境中不提供编译器。也许是在JRE而不是JDK上运行?
Java用相同的方法在一个类中实现两个接口。哪种接口方法被覆盖?
Java 什么是Runtime.getRuntime()。totalMemory()和freeMemory()?
java.library.path中的java.lang.UnsatisfiedLinkError否*****。dll
JavaFX“位置是必需的。” 即使在同一包装中
Java 导入两个具有相同名称的类。怎么处理?
Java 是否应该在HttpServletResponse.getOutputStream()/。getWriter()上调用.close()?
Java RegEx元字符(。)和普通点?