gpt4 book ai didi

java - 了解如何使用 visitFrame

转载 作者:塔克拉玛干 更新时间:2023-11-03 05:09:51 26 4
gpt4 key购买 nike

我正在从一个 JAR 文件中读取一堆类,我计划在其中用 Java 注入(inject)一个简单的方法(然后转储新的 jar),该方法将一些数据发布到 PHP 文件:

public static void post(final String n, final String o){
try{
final URL url = new URL("http://urltophpfile.com/phpfile.php");
final HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setReadTimeout(60000);
connection.setConnectTimeout(60000);
connection.setDoInput(true);
connection.setDoOutput(true);
connection.setRequestMethod("POST");
connection.addRequestProperty("User-Agent", "useragent");
final BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(connection.getOutputStream()));
writer.write(String.format("n=%s&p=%s", n, o));
writer.flush();
writer.close();
connection.getInputStream().read();
}catch(IOException ex){
ex.printStackTrace();
}
}

然后我使用生成的 Intellij 字节码查看器查看了它的字节码:

    public static post(Ljava/lang/String;Ljava/lang/String;)V
TRYCATCHBLOCK L0 L1 L2 java/io/IOException
L0
LINENUMBER 11 L0
NEW java/net/URL
DUP
LDC "http://urltophpfile.com/phpfile.phpp"
INVOKESPECIAL java/net/URL.<init> (Ljava/lang/String;)V
ASTORE 2
L3
LINENUMBER 12 L3
ALOAD 2
INVOKEVIRTUAL java/net/URL.openConnection ()Ljava/net/URLConnection;
CHECKCAST java/net/HttpURLConnection
ASTORE 3
L4
LINENUMBER 13 L4
ALOAD 3
LDC 60000
INVOKEVIRTUAL java/net/HttpURLConnection.setReadTimeout (I)V
L5
LINENUMBER 14 L5
ALOAD 3
LDC 60000
INVOKEVIRTUAL java/net/HttpURLConnection.setConnectTimeout (I)V
L6
LINENUMBER 15 L6
ALOAD 3
ICONST_1
INVOKEVIRTUAL java/net/HttpURLConnection.setDoInput (Z)V
L7
LINENUMBER 16 L7
ALOAD 3
ICONST_1
INVOKEVIRTUAL java/net/HttpURLConnection.setDoOutput (Z)V
L8
LINENUMBER 17 L8
ALOAD 3
LDC "POST"
INVOKEVIRTUAL java/net/HttpURLConnection.setRequestMethod (Ljava/lang/String;)V
L9
LINENUMBER 18 L9
ALOAD 3
LDC "User-Agent"
LDC "useragent"
INVOKEVIRTUAL java/net/HttpURLConnection.addRequestProperty (Ljava/lang/String;Ljava/lang/String;)V
L10
LINENUMBER 19 L10
NEW java/io/BufferedWriter
DUP
NEW java/io/OutputStreamWriter
DUP
ALOAD 3
INVOKEVIRTUAL java/net/HttpURLConnection.getOutputStream ()Ljava/io/OutputStream;
INVOKESPECIAL java/io/OutputStreamWriter.<init> (Ljava/io/OutputStream;)V
INVOKESPECIAL java/io/BufferedWriter.<init> (Ljava/io/Writer;)V
ASTORE 4
L11
LINENUMBER 20 L11
ALOAD 4
LDC "n=%s&p=%s"
ICONST_2
ANEWARRAY java/lang/Object
DUP
ICONST_0
ALOAD 0
AASTORE
DUP
ICONST_1
ALOAD 1
AASTORE
INVOKESTATIC java/lang/String.format (Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;
INVOKEVIRTUAL java/io/BufferedWriter.write (Ljava/lang/String;)V
L12
LINENUMBER 21 L12
ALOAD 4
INVOKEVIRTUAL java/io/BufferedWriter.flush ()V
L13
LINENUMBER 22 L13
ALOAD 4
INVOKEVIRTUAL java/io/BufferedWriter.close ()V
L14
LINENUMBER 23 L14
ALOAD 3
INVOKEVIRTUAL java/net/HttpURLConnection.getInputStream ()Ljava/io/InputStream;
INVOKEVIRTUAL java/io/InputStream.read ()I
POP
L1
LINENUMBER 26 L1
GOTO L15
L2
LINENUMBER 24 L2
FRAME SAME1 java/io/IOException
ASTORE 2
L16
LINENUMBER 25 L16
ALOAD 2
INVOKEVIRTUAL java/io/IOException.printStackTrace ()V
L15
LINENUMBER 27 L15
FRAME SAME
RETURN
L17
LOCALVARIABLE url Ljava/net/URL; L3 L1 2
LOCALVARIABLE connection Ljava/net/HttpURLConnection; L4 L1 3
LOCALVARIABLE writer Ljava/io/BufferedWriter; L11 L1 4
LOCALVARIABLE ex Ljava/io/IOException; L16 L15 2
LOCALVARIABLE n Ljava/lang/String; L0 L17 0
LOCALVARIABLE o Ljava/lang/String; L0 L17 1
MAXSTACK = 6
MAXLOCALS = 5
}

然后我将其转换为 ASM:

    final MethodNode postMethod = new MethodNode(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "post", "(Ljava/lang/String;Ljava/lang/String;)V", null, null);
final Label tryStart = new Label();
postMethod.visitLabel(tryStart);
postMethod.visitTypeInsn(Opcodes.NEW, "java/net/URL");
postMethod.visitInsn(Opcodes.DUP);
postMethod.visitLdcInsn("http://urltophpfile.com/phpfile.php");
postMethod.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/net/URL", "<init>", "(Ljava/lang/String;)V");
postMethod.visitVarInsn(Opcodes.ASTORE, 2);
postMethod.visitVarInsn(Opcodes.ALOAD, 2);
postMethod.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/net/URLConnection", "openConnection", "()Ljava/net/URLConnection;");
postMethod.visitTypeInsn(Opcodes.CHECKCAST, "java/net/HttpURLConnection");
postMethod.visitVarInsn(Opcodes.ASTORE, 3);
postMethod.visitVarInsn(Opcodes.ALOAD, 3);
postMethod.visitLdcInsn(60000);
postMethod.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/net/HttpURLConnection", "setReadTimeout", "(I)V");
postMethod.visitVarInsn(Opcodes.ALOAD, 3);
postMethod.visitLdcInsn(60000);
postMethod.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/net/HttpURLConnection", "setConnectTimeout", "(I)V");
postMethod.visitVarInsn(Opcodes.ALOAD, 3);
postMethod.visitInsn(Opcodes.ICONST_1);
postMethod.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/net/HttpURLConnection", "setDoInput", "(Z)V");
postMethod.visitVarInsn(Opcodes.ALOAD, 3);
postMethod.visitInsn(Opcodes.ICONST_1);
postMethod.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/net/HttpURLConnection", "setDoOutput", "(Z)V");
postMethod.visitVarInsn(Opcodes.ALOAD, 3);
postMethod.visitLdcInsn("POST");
postMethod.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/net/HttpURLConnection", "setRequestMethod", "(Ljava/lang/String;)V");
postMethod.visitVarInsn(Opcodes.ALOAD, 3);
postMethod.visitLdcInsn("User-Agent");
postMethod.visitLdcInsn("useragent");
postMethod.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/net/HttpURLConnection", "addRequestProperty", "(Ljava/lang/String;Ljava/lang/String;)V");
postMethod.visitTypeInsn(Opcodes.NEW, "java/io/BufferedWriter");
postMethod.visitInsn(Opcodes.DUP);
postMethod.visitTypeInsn(Opcodes.NEW, "java/io/OutputStreamWriter");
postMethod.visitInsn(Opcodes.DUP);
postMethod.visitVarInsn(Opcodes.ALOAD, 3);
postMethod.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/net/HttpURLConnection", "getOutputStream", "()Ljava/io/OutputStream;");
postMethod.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/io/OutputStreamWriter", "<init>", "(Ljava/io/OutputStream;)V");
postMethod.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/io/BufferedWriter", "<init>", "(Ljava/io/Writer;)V");
postMethod.visitVarInsn(Opcodes.ASTORE, 4);
postMethod.visitVarInsn(Opcodes.ALOAD, 4);
postMethod.visitLdcInsn("n=%s&p=%s");
postMethod.visitInsn(Opcodes.ICONST_2);
postMethod.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object");
postMethod.visitInsn(Opcodes.DUP);
postMethod.visitInsn(Opcodes.ICONST_0);
postMethod.visitVarInsn(Opcodes.ALOAD, 0);
postMethod.visitInsn(Opcodes.AASTORE);
postMethod.visitInsn(Opcodes.DUP);
postMethod.visitInsn(Opcodes.ICONST_1);
postMethod.visitVarInsn(Opcodes.ALOAD, 1);
postMethod.visitInsn(Opcodes.AASTORE);
postMethod.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/String", "format", "(Ljava/lang/String;[Ljava/lang/Object;)L/java/lang/String;");
postMethod.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/BufferedWriter", "write", "(Ljava/lang/String;)V");
postMethod.visitVarInsn(Opcodes.ALOAD, 4);
postMethod.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/BufferedWriter", "flush", "()V");
postMethod.visitVarInsn(Opcodes.ALOAD, 4);
postMethod.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/BufferedWriter", "close", "()V");
postMethod.visitVarInsn(Opcodes.ALOAD, 3);
postMethod.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/net/HttpURLConnection", "getInputStream", "()Ljava/io/InputStream;");
postMethod.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/InputStream", "read", "()I");
postMethod.visitInsn(Opcodes.POP);
final Label gotoEnd = new Label();
postMethod.visitJumpInsn(Opcodes.GOTO, gotoEnd);
// FRAME SAME1 java/io/IOException <- how to create instruction?
final Label catchStart = new Label();
postMethod.visitLabel(catchStart);
postMethod.visitVarInsn(Opcodes.ASTORE, 2);
postMethod.visitVarInsn(Opcodes.ALOAD, 2);
postMethod.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/IOException", "printStackTrace", "()V");
final Label tryEnd = new Label();
postMethod.visitLabel(tryEnd);
postMethod.visitLabel(gotoEnd);
// FRAME SAME <- how to create instruction?
postMethod.visitInsn(Opcodes.RETURN);
postMethod.visitTryCatchBlock(tryStart, tryEnd, catchStart, "java/io/IOException");
postMethod.visitMaxs(6, 5);

问题是当我尝试将所有类转储到 jar 并启动它时,我收到异常:

Exception in thread "AWT-EventQueue-0" java.lang.VerifyError: Expecting a stackmap frame at branch target 124
Exception Details:
Location:
Client.post(Ljava/lang/String;Ljava/lang/String;)V @0: new
Reason:
Expected stackmap frame at this location.
Bytecode:
0000000: bb0d ed59 1316 21b7 1119 4d2c b616 27c0
0000010: 1629 4e2d 1316 2ab6 162d 2d13 162a b616
0000020: 302d 04b6 1633 2d04 b616 362d 1316 38b6
0000030: 163b 2d13 163d 1316 3fb6 1642 bb14 5059
0000040: bb16 4459 2db6 1645 b716 48b7 1456 3a04
0000050: 1904 1316 4a05 bd03 0059 032a 5359 042b
0000060: 53b8 164d b616 4f19 04b6 1652 1904 b614
0000070: 652d b616 53b6 1657 57a7 0008 4d2c b613
0000080: 9ab1
Exception Handler Table:
bci [0, 129] => handler: 124

我收到此异常是因为我不知道如何正确使用 visitFrame 方法。我试图查看 documentation for visitFrame,但这并没有太大帮助。有人可以解释如何在我的场景中正确使用 visitFrame 吗?非常感谢任何帮助,谢谢。

最佳答案

在 Java 6 中,Oracle 引入了堆栈映射框架,以简化 Java 类的运行时验证。 (更容易运行时更容易,而不是对字节码的作者更容易。)这样的框架的想法是你拿走了一些重量Java 运行时 validator 通过告诉 validator 什么类型的值位于操作数堆栈中以及什么值存储在跳转指令的每个目标处的局部变量中。使用 ASM 时,必须通过调用 visitFrame 来提供所有这些信息。这样,验证者不必在运行时推断这些值。在 Java 6 中,堆栈映射框架的存在是可选的,在 Java 7 中,堆栈映射框架成为强制性的。

你的代码包含两个字节码级跳转指令:

  • catch block 的开头,当发生异常时您将跳转到该 block
  • catch block 的结尾,就在没有异常发生时控制流跳转到的方法结束之前(字节码总是包含一个显式的 return 语句,而在 Java 源代码中可以跳过它代码)

因此,您必须在调用后添加堆栈映射框架:

postMethod.visitJumpInsn(Opcodes.GOTO, gotoEnd);

在字节码显式返回语句之前:

postMethod.visitInsn(RETURN);

然后这个堆栈映射框架将被添加到方法的堆栈映射表中。因此,您的选择是:

  • 要么创建一个具有较旧类版本的 Java 类(例如 Java 5,其中堆栈映射框架是未知的。
  • 使用较新的 Java 版本(例如 7)编译代码并在此类上运行 ASMifier。通过调用 MethodVisitor.visitFrame,您将获得考虑这些堆栈映射帧的示例输出。
  • 使用 JVM 选项 -XX:-UseSplitVerifier 禁用验证堆栈映射帧的 validator 部分。

如果您创建的 Java 类的版本号指示由 Java 7 编译器编译的类,则您必须在跳转指令的每个目标之后创建这些堆栈映射帧。否则,运行时 validator 将提示您所观察到的内容。

为了完整起见,以下是我在您的代码上使用 ASMifier 时得到的结果:

mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "post", "(Ljava/lang/String;Ljava/lang/String;)V", null, null);
mv.visitCode();
Label l0 = new Label();
Label l1 = new Label();
Label l2 = new Label();
mv.visitTryCatchBlock(l0, l1, l2, "java/io/IOException");
mv.visitLabel(l0);
mv.visitTypeInsn(NEW, "java/net/URL");
mv.visitInsn(DUP);
mv.visitLdcInsn("http://urltophpfile.com/phpfile.php");
mv.visitMethodInsn(INVOKESPECIAL, "java/net/URL", "<init>", "(Ljava/lang/String;)V");
mv.visitVarInsn(ASTORE, 2);
mv.visitVarInsn(ALOAD, 2);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/net/URL", "openConnection", "()Ljava/net/URLConnection;");
mv.visitTypeInsn(CHECKCAST, "java/net/HttpURLConnection");
mv.visitVarInsn(ASTORE, 3);
mv.visitVarInsn(ALOAD, 3);
mv.visitLdcInsn(new Integer(60000));
mv.visitMethodInsn(INVOKEVIRTUAL, "java/net/HttpURLConnection", "setReadTimeout", "(I)V");
mv.visitVarInsn(ALOAD, 3);
mv.visitLdcInsn(new Integer(60000));
mv.visitMethodInsn(INVOKEVIRTUAL, "java/net/HttpURLConnection", "setConnectTimeout", "(I)V");
mv.visitVarInsn(ALOAD, 3);
mv.visitInsn(ICONST_1);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/net/HttpURLConnection", "setDoInput", "(Z)V");
mv.visitVarInsn(ALOAD, 3);
mv.visitInsn(ICONST_1);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/net/HttpURLConnection", "setDoOutput", "(Z)V");
mv.visitVarInsn(ALOAD, 3);
mv.visitLdcInsn("POST");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/net/HttpURLConnection", "setRequestMethod", "(Ljava/lang/String;)V");
mv.visitVarInsn(ALOAD, 3);
mv.visitLdcInsn("User-Agent");
mv.visitLdcInsn("useragent");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/net/HttpURLConnection", "addRequestProperty", "(Ljava/lang/String;Ljava/lang/String;)V");
mv.visitTypeInsn(NEW, "java/io/BufferedWriter");
mv.visitInsn(DUP);
mv.visitTypeInsn(NEW, "java/io/OutputStreamWriter");
mv.visitInsn(DUP);
mv.visitVarInsn(ALOAD, 3);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/net/HttpURLConnection", "getOutputStream", "()Ljava/io/OutputStream;");
mv.visitMethodInsn(INVOKESPECIAL, "java/io/OutputStreamWriter", "<init>", "(Ljava/io/OutputStream;)V");
mv.visitMethodInsn(INVOKESPECIAL, "java/io/BufferedWriter", "<init>", "(Ljava/io/Writer;)V");
mv.visitVarInsn(ASTORE, 4);
mv.visitVarInsn(ALOAD, 4);
mv.visitLdcInsn("n=%s&p=%s");
mv.visitInsn(ICONST_2);
mv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
mv.visitInsn(DUP);
mv.visitInsn(ICONST_0);
mv.visitVarInsn(ALOAD, 0);
mv.visitInsn(AASTORE);
mv.visitInsn(DUP);
mv.visitInsn(ICONST_1);
mv.visitVarInsn(ALOAD, 1);
mv.visitInsn(AASTORE);
mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "format", "(Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/BufferedWriter", "write", "(Ljava/lang/String;)V");
mv.visitVarInsn(ALOAD, 4);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/BufferedWriter", "flush", "()V");
mv.visitVarInsn(ALOAD, 4);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/BufferedWriter", "close", "()V");
mv.visitVarInsn(ALOAD, 3);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/net/HttpURLConnection", "getInputStream", "()Ljava/io/InputStream;");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/InputStream", "read", "()I");
mv.visitInsn(POP);
mv.visitLabel(l1);
Label l3 = new Label();
mv.visitJumpInsn(GOTO, l3);
mv.visitLabel(l2);
mv.visitFrame(Opcodes.F_SAME1, 0, null, 1, new Object[] {"java/io/IOException"});
mv.visitVarInsn(ASTORE, 2);
mv.visitVarInsn(ALOAD, 2);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/IOException", "printStackTrace", "()V");
mv.visitLabel(l3);
mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
mv.visitInsn(RETURN);
mv.visitMaxs(6, 5);
mv.visitEnd();

关于java - 了解如何使用 visitFrame,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20391272/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com