I am reading in a bunch of classes from a JAR file in which I plan to inject a simple method (and then dump the new jar) in Java which posts some data to a PHP file:
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();
}
}
Then I viewed the bytecode for it using the Intellij Bytecode Viewer which produced:
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
}
Which I then converted to 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);
The problem is that when I try dumping all of the classes to the jar and starting it, I receive the exception:
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
I receive this exception because I don't know how to properly use the visitFrame
method. I've tried to look at the documentation for visitFrame
but that didn't help much. Can someone explain how to properly use visitFrame
in my scenario? Any help would be greatly appreciated, thanks.
With Java 6 Oracle introduced stack map frames to make run time validation of Java classes easier. (Easier as in easier for the run time, not as in easier for the author of byte code.) The idea of such frames is that you take away some weight of the Java run time validator by telling the validator what kind of values are lying on the operand stack and what values are stored in the local variables at each target of a jump instruction. All this information has to be provided by calling visitFrame
when using ASM. This way, the verfifier does not have to infer these values at run time. In Java 6 the existence of stack map frames was optional, in Java 7, stack map frames became mandatory.
Your code contains two byte code level jump instruction:
catch
block where you would jump to when an exception occurescatch
block right before the end of the method where the control flow jumps to when no exception occurs (byte code always contains an explicit return statement while it can be skipped in Java source code)Therefore, you would have to add a stack map frame after calling:
postMethod.visitJumpInsn(Opcodes.GOTO, gotoEnd);
and before the explicit return statement in the byte code:
postMethod.visitInsn(RETURN);
This stack map frame would then be added to the method's stack map table. Your options are therefore:
ASMifier
on this class. You will get an example output considering these stack map frames by calls to MethodVisitor.visitFrame
.-XX:-UseSplitVerifier
.If you are creating Java classes with a version number indicating a class compiled by a Java 7 compiler, you must create these stack map frames after each target of a jump instruction. Otherwise, the run time verifier will complain what is exactly what you observe.
For the sake of completeness, here is what I get when using ASMifier
on your code:
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();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With