我正在读取JAR文件中的一堆类,我计划在Java中注入一个简单的方法(然后转储新的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 Bytecode Viewer查看了它的字节码,产生了:
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
吗?非常感谢任何帮助,谢谢。
答案 0 :(得分:7)
借助Java 6,Oracle引入了堆栈映射框,使Java类的运行时验证更加容易。 (在更容易运行时更容易,而不像更容易为字节代码的作者。)这样的框架的想法是你带走了一些重量的Java运行时验证程序通过告诉验证器操作数堆栈上有哪种值以及在跳转指令的每个目标的本地变量中存储了哪些值。使用ASM时,必须通过调用visitFrame
来提供所有这些信息。这样,验证器不必在运行时推断这些值。在Java 6中,堆栈映射帧的存在是可选的,在Java 7中,堆栈映射帧变为必需的。
您的代码包含两个字节代码级跳转指令:
catch
块的开头catch
块的末尾,当没有异常发生时控制流跳转到(当在Java源代码中可以跳过时,字节代码总是包含显式返回语句)< / LI>
因此,您必须在调用后添加堆栈映射框:
postMethod.visitJumpInsn(Opcodes.GOTO, gotoEnd);
并在字节代码中显式返回语句之前:
postMethod.visitInsn(RETURN);
然后,此堆栈映射框将添加到方法的堆栈映射表中。因此,您的选择是:
ASMifier
。您将通过调用MethodVisitor.visitFrame
来获取考虑这些堆栈映射帧的示例输出。-XX:-UseSplitVerifier
禁用验证程序验证堆栈映射框的部分。如果要创建具有指示由Java 7编译器编译的类的版本号的Java类,则必须在跳转指令的每个目标之后创建这些堆栈映射帧。否则,运行时验证程序会抱怨你观察到的是什么。
为了完整起见,这是我在您的代码上使用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();