Mastering java bytecode with ASM - GeeCON 2012

4,164
-1

Published on

Published in: Technology, Education
1 Comment
30 Likes
Statistics
Notes
No Downloads
Views
Total Views
4,164
On Slideshare
0
From Embeds
0
Number of Embeds
4
Actions
Shares
0
Downloads
203
Comments
1
Likes
30
Embeds 0
No embeds

No notes for slide
  • Why would learn Java bytecode and ASM at all?Well, creating a brand new JVM language might be a good idea  This is what all the cool kids are doing, right?Secondly – programming model that is exposed by many frameworks is backed with bytecode generation or instrumentation. AspectJ, for instance uses bytecode instrumentation extensively.Also, there are some many awesome tools that do awesome stuff, like …. JRebel for instance 
  • This presentation provides some pointers to the subject
  • Actually, before going further, let’s mention javap– the Java class disassembler. The output of javap command isn’t particularly useful as there’s no way to modify it and compile back to the executable code. However, it is much more easier to read the bytecodes produced by javap, rather than the code that is written using ASM API. So, javap is good for reference when studying the bytecode.
  • The most common scenario to generate bytecode that corresponds to the example source, is to create ClassWriter, visit the structure – fields, methods, etc, and after the job is done, write out the final bytes.
  • Let’s go on and construct a ClassWriter.. The constructor takes anint which is composed of different flags.COMPUTE_MAXS says that the sizes of local variables and operand stack parts will be computed automatically. Still have to call visitMaxs with any argumentsCOMPUTE_FRAMES – everything is computed automatically. ASM will compute the stack map, still have to call visitMaxs
  • ... using ASMifierClassVisitor (ASMifier in ASM4). The output is rather noisy, with all the labels,but the noise is easy to remove.
  • Mastering java bytecode with ASM - GeeCON 2012

    1. 1. Mastering Java Bytecode with ASMLearn some bytecode to yourself!
    2. 2. whoamiAnton ArhipovJava Dev / Product LeadZeroTurnaround, JRebelMessing with bytecode since 2010anton@zeroturnaround.com@antonarhipov @javarebel
    3. 3. whoamiAnton ArhipovJava Dev / Product LeadZeroTurnaround, JRebelMessing with bytecode since 2010anton@zeroturnaround.com@antonarhipov @javarebel
    4. 4. Why Bytecode?• Know your platform!• Build your own JVM language?• Programming models (AOP, ORM)• Awesome tools (like JRebel )... just bored?
    5. 5. Bytecode 101 Instrumentation API javap ObjectWeb ASM
    6. 6. Bytecode 101 Gentle introduction
    7. 7. Adding Two ValuesA+B
    8. 8. Adding Two ValuesA+BAB+
    9. 9. Adding Two ValuesA+BAB+
    10. 10. Adding Two ValuesA+B PUSH A AAB+
    11. 11. Adding Two ValuesA+B PUSH 1 PUSH 2 BAB+ A
    12. 12. Adding Two ValuesA+B PUSH 1 PUSH 2 15AB+ ADD
    13. 13. Adding Two ValuesA+B ICONST_1 ICONST_2 15AB+ IADD
    14. 14. TYPE OPERATION• <TYPE> ::= b, s, c, i, l, f, d, a• constant values (ldc, iconst_1)• Local variables and stack interaction (load/store)• Array operations (aload, astore)• Math (add, sub, mul, div)• Boolean/bitwise operations (iand, ixor)• Comparisons & branching (cmpl, ifeq, jsr, tableswitch)• Conversions (l2d, i2l)
    15. 15. Model ofExecution
    16. 16. Enter JVMJVM process
    17. 17. Enter ThreadsThread A Thread B Thread C Thread D
    18. 18. Enter Frames
    19. 19. The FrameLocal variables0 1 2 … NOperand stack #1 Constant Pool
    20. 20. Juggling The Stack
    21. 21. Juggling The Stackdup Apop Bswapdup_x1dup2_x1
    22. 22. Juggling The Stackdup Apop Aswap Bdup_x1dup2_x1
    23. 23. Juggling The Stackdup Apop Bswapdup_x1dup2_x1
    24. 24. Juggling The Stackdup Bpop Aswapdup_x1dup2_x1
    25. 25. Juggling The Stackdup Bpop Aswap Bdup_x1dup2_x1
    26. 26. Juggling The Stackdup Bpop Aswap Bdup_x1 Bdup2_x1 A
    27. 27. Local Variables
    28. 28. Local Variables Stackvar value depth value0 ldc "Hello" 0 astore_01 1 iconst_12 astore_1 23 aload_0 34 4
    29. 29. Local Variables Stackvar value depth value0 ldc "Hello" 0 "Hello" astore_01 1 iconst_12 astore_1 23 aload_0 34 4
    30. 30. Local Variables Stackvar value depth value0 ldc "Hello" 0 "Hello" astore_01 1 iconst_12 astore_1 23 aload_0 34 4
    31. 31. Local Variables Stackvar value depth value0 ldc "Hello" 0 "Hello" 1 astore_01 1 iconst_12 astore_1 23 aload_0 34 4
    32. 32. Local Variables Stackvar value depth value0 ldc "Hello" 0 "Hello" astore_01 1 1 iconst_12 astore_1 23 aload_0 34 4
    33. 33. Local Variables Stackvar value depth value0 ldc "Hello" 0 "Hello" "Hello" astore_01 1 1 iconst_12 astore_1 23 aload_0 34 4
    34. 34. load LocalVariables Stack Table store
    35. 35. Method Invoсation
    36. 36. Method Invocationobj.method(param1, param2);
    37. 37. Method Invocationobj.method(param1, param2); push obj push param1 push param2 invoke method
    38. 38. Method Invocationobj.method(param1, param2); obj push obj push param1 push param2 invoke method
    39. 39. Method Invocationobj.method(param1, param2); param1 push obj obj push param1 push param2 invoke method
    40. 40. Method Invocationobj.method(param1, param2); param2 push obj param1 push param1 obj push param2 invoke method
    41. 41. Method Invocationobj.method(param1, param2); obj? push obj push param1 push param2 invoke method
    42. 42. Operator Overloading
    43. 43. Operator Overloading[int] A + B [Foo] A.plus(B)
    44. 44. Operator Overloading[int] A + B [Foo] A.plus(B) push A push A push B push B iadd invokevirtual plus
    45. 45. Operator Overloading[int] A + B [Foo] A + B push A push A push B push B iadd invokevirtual plus
    46. 46. pop push Stack
    47. 47. pop push load LocalVariables Stack Table store
    48. 48. pop push load LocalVariables Stack Table store
    49. 49. pop push load LocalVariables Stack invoke Table store pop push load Local Variables Stack Table store
    50. 50. javapThe disassembler
    51. 51. javap• Java class file disassembler• Used with no options shows class structure only – Methods, superclass, interfaces, etc• -c shows the bytecode• -private shows all methods and members• -s prints internal signatures• -l prints line numbers and local variable tables• -verbose for verbosity 
    52. 52. C:workgeeconclasses>javap Hello -c
    53. 53. C:workgeeconclasses>javap Hello -cCompiled from "Hello.java"public class Hello extends java.lang.Object{public Hello(); Code: the default constructor 0: aload_0 1: invokespecial #1; //Method java/lang/Object."<init>":()V 4: return
    54. 54. C:workgeeconclasses>javap Hello -cCompiled from "Hello.java"public class Hello extends java.lang.Object{public Hello(); Code: push this to stack 0: aload_0 1: invokespecial #1; //Method java/lang/Object."<init>":()V 4: return
    55. 55. C:workgeeconclasses>javap Hello -cCompiled from "Hello.java"public class Hello extends java.lang.Object{public Hello(); Code: 0: aload_0 1: invokespecial #1; //Method java/lang/Object."<init>":()V 4: return invoke <init> on this
    56. 56. C:workgeeconclasses>javap Hello -cCompiled from "Hello.java"public class Hello extends java.lang.Object{public Hello(); Code: 0: aload_0 1: invokespecial #1; //Method java/lang/Object."<init>":()V super() 4: return
    57. 57. C:workgeeconclasses>javap Hello -cCompiled from "Hello.java"public class Hello extends java.lang.Object{public Hello(); Code: 0: aload_0 1: invokespecial #1; //Method java/lang/Object."<init>":()V 4: return
    58. 58. C:workgeeconclasses>javap Hello -cCompiled from "Hello.java"public class Hello extends java.lang.Object{public Hello(); Code: 0: aload_0 1: invokespecial #1; //Method java/lang/Object."<init>":()V 4: returnpublic static void main(java.lang.String[]); Code: 0: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #3; //String Hello, World! 5: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
    59. 59. C:workgeeconclasses>javap Hello -cCompiled from "Hello.java"public class Hello extends java.lang.Object{public Hello(); Code: 0: aload_0 1: invokespecial #1; //Method java/lang/Object."<init>":()V 4: return get static fieldpublic static void main(java.lang.String[]); Code: 0: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #3; //String Hello, World! 5: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
    60. 60. C:workgeeconclasses>javap Hello -cCompiled from "Hello.java"public class Hello extends java.lang.Object{public Hello(); Code: 0: aload_0 1: invokespecial #1; //Method java/lang/Object."<init>":()V 4: returnpublic static void main(java.lang.String[]); Code: 0: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #3; //String Hello, World! 5: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V load string to the stack
    61. 61. C:workgeeconclasses>javap Hello -cCompiled from "Hello.java"public class Hello extends java.lang.Object{public Hello(); Code: 0: aload_0 1: invokespecial #1; //Method java/lang/Object."<init>":()V 4: returnpublic static void main(java.lang.String[]); Code: 0: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #3; //String Hello, World! 5: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V invoke method with parameter
    62. 62. C:workgeeconclasses>javap Hello -cCompiled from "Hello.java"public class Hello extends java.lang.Object{public Hello(); Code: 0: aload_0 1: invokespecial #1; //Method java/lang/Object."<init>":()V 4: returnpublic static void main(java.lang.String[]); Code: 0: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #3; //String Hello, World! 5: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
    63. 63. What’s #1,#2, etc ?C:workgeeconclasses>javap Hello -cCompiled from "Hello.java"public class Hello extends java.lang.Object{public Hello(); Code: 0: aload_0 1: invokespecial #1; //Method java/lang/Object."<init>":()V 4: returnpublic static void main(java.lang.String[]); Code: 0: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #3; //String Hello, World! 5: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
    64. 64. SLIDESGOTO: IDESLIDESIDE: JAVAP DEMO
    65. 65. ASMThe de facto standard for bytecode manipulation
    66. 66. ASM• “All purpose bytecode manipulation and analysis framework”• De facto standard bytecode library• http://asm.ow2.org
    67. 67. Basic Process• Construct ClassWriter• Stack up the visitors for: • annotations, methods, fields, etc• Write out bytes
    68. 68. Hello.java
    69. 69. ClassWriterClassWriter cw = new ClassWriter( ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
    70. 70. COMPUTE_***• COMPUTE_MAXS –ASM will calculate max stack/local vars• COMPUTE_FRAMES –ASM will calculate Java 6 stack map
    71. 71. Visit Classcv.visit(V1_6, ACC_PUBLIC, "X", null, "java/lang/Object", null);
    72. 72. Opcodes• Interface full of constants –Bytecodes –Visibility modifiers –Java versions –Other stuff
    73. 73. ACC_***• Some you know –ACC_PUBLIC, ACC_ABSTRACT, etc• Some you (probably) don’t –ACC_BRIDGE, ACC_SYNTHETIC
    74. 74. Class Names "java/lang/Object"packageClass.replaceAll(., /)
    75. 75. Type Descriptors
    76. 76. Type Descriptors B byte C char S string I int J long F float D double Z boolean V void
    77. 77. Type DescriptorsLsome/Class;
    78. 78. Type Descriptors[Lsome/Class;
    79. 79. Method Signatures()V void foo()(Ljava/lang/Object;)I int foo(Object)([Ljava/lang/String;)V void main(String[])
    80. 80. Visit MethodMethodVisitor constructor = cv.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);MethodVisitor mv = cv.visitMethod( ACC_PUBLIC + ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null);
    81. 81. Visit MethodMethodVisitor constructor = cv.visitMethod(ACC_PUBLIC, "<init>", Wat!? o_O "()V", null, null);MethodVisitor mv = cv.visitMethod( ACC_PUBLIC + ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null);
    82. 82. Special Methods• <init> –Constructor• <clinit> –Static initializer
    83. 83. MethodVisitor• Visit annotations• Visit code – Bytecodes, local variables, line numbers, etc• Visit maxs – Pass bogus values if COMPUTE_MAX
    84. 84. Constructorc.visitVarInsn(ALOAD, 0);c.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");c.visitInsn(RETURN);c.visitMaxs(0, 0);
    85. 85. Constructorc.visitVarInsn(ALOAD, 0);c.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");c.visitInsn(RETURN);c.visitMaxs(0, 0); aload_0 invokespecial return
    86. 86. public static void main()
    87. 87. public static void main()mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");mv.visitLdcInsn("Hello");mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V");mv.visitInsn(RETURN);
    88. 88. public static void main()mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", getstatic "Ljava/io/PrintStream;"); ldc “Hello” invokevirtualmv.visitLdcInsn("Hello"); returnmv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V");mv.visitInsn(RETURN);
    89. 89. Enter Loops
    90. 90. Enter Loopsstart: int i = 0loop: print "Hello" i=i+1 if i < 10 goto loopend: return
    91. 91. Enter Loopsstart: int i = 0 GOTO isn’t harmful ;)loop: print "Hello" i=i+1 if i < 10 goto loopend: return
    92. 92. Enter Loops0: iconst_01: istore_12: iload_13: bipush 105: if_icmpge 22System.out.println(“Hello”)16: iinc 1, 119: goto 222: return
    93. 93. Enter Loopsstart: iconst_0 1: istore_1loop: iload_1 3: bipush 10 5: if_icmpge end System.out.println(“Hello”) 16: iinc 1, 1 19: goto loopend: return
    94. 94. Enter Loopsstart: iconst_0 1: istore_1 int i = 0loop: iload_1 3: bipush 10 5: if_icmpge end System.out.println(“Hello”) 16: iinc 1, 1 19: goto loopend: return
    95. 95. Enter Loopsstart: iconst_0 1: istore_1loop: iload_1 3: bipush 10 i < 10 5: if_icmpge end System.out.println(“Hello”) 16: iinc 1, 1 19: goto loopend: return
    96. 96. Enter Loopsstart: iconst_0 1: istore_1loop: iload_1 3: bipush 10 5: if_icmpge end System.out.println(“Hello”) 16: iinc 1, 1 i++ 19: goto loopend: return
    97. 97. Enter Loopsstart: iconst_0 1: istore_1loop: iload_1 3: bipush 10 5: if_icmpge end System.out.println(“Hello”) 16: iinc 1, 1 19: goto loopend: return
    98. 98. Enter ASM LoopsLabel start = new Label();Label loop = new Label();Label end = new Label();// i = 0mv.visitLabel(start);mv.visitInsn(ICONST_0);mv.visitVarInsn(ISTORE, 1);
    99. 99. Enter ASM LoopsLabel start = new Label();Label loop = new Label();Label end = new Label();// i = 0mv.visitLabel(start);mv.visitInsn(ICONST_0);mv.visitVarInsn(ISTORE, 1);// i < 10mv.visitLabel(loop);mv.visitVarInsn(ILOAD, 1);mv.visitLdcInsn(10);mv.visitJumpInsn(IF_ICMPGE, end);
    100. 100. Enter ASM LoopsLabel start = new Label();Label loop = new Label();Label end = new Label(); //increment & continue the loop mv.visitIincInsn(1, 1);// i = 0 mv.visitJumpInsn(GOTO, loop);mv.visitLabel(start); mv.visitLabel(end);mv.visitInsn(ICONST_0);mv.visitVarInsn(ISTORE, 1);// i < 10mv.visitLabel(loop);mv.visitVarInsn(ILOAD, 1);mv.visitLdcInsn(10);mv.visitJumpInsn(IF_ICMPGE, end);
    101. 101. ClassWriter
    102. 102. geecon$ java HelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHello
    103. 103. ASMified ASMifierClassVisitormv = cw.visitMethod(ACC_PUBLIC, "getId", "(I)I", null, null);mv.visitCode();Label l0 = new Label();mv.visitLabel(l0);mv.visitLineNumber(16, l0);mv.visitVarInsn(ALOAD, 0);mv.visitFieldInsn(GETFIELD, "zt/asm/Items", "ids", "Ljava/util/List;"); java -cp asm-all-3.3.1.jar:asm-util-3.3.1.jar mv.visitVarInsn(ILOAD, 1); org.objectweb.asm.util.ASMifierClassVisitor mv.visitMethodInsn(INVOKEINTERFACE, "java/util/List", "get", "(I)Ljava/lang/Object;"); Hello.classmv.visitTypeInsn(CHECKCAST, "java/lang/Integer");mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Integer", "intValue", "()I");mv.visitInsn(IRETURN);Label l1 = new Label();mv.visitLabel(l1);mv.visitLocalVariable("this", "Lzt/asm/Items;", null, l0, l1, 0);mv.visitLocalVariable("i", "I", null, l0, l1, 1);mv.visitMaxs(2, 2);mv.visitEnd();
    104. 104. Bytecodeinstrumentation Some magic for your own good
    105. 105. WAT!?Ninja.class Ninja.class’10101010101 1010101010111000101010 1110000101010101010001 1010101000100010001110 0001000111011011101011 11011101110
    106. 106. Who? Containers (Java EE, Spring) TerracottaJRebel Tapestry Byteman
    107. 107. How?• Add –javaagent to hook into class loading process• Implement ClassFileTransformer• Use bytecode manipulation libraries (Javassist, cglib, asm) to add any custom logic java.lang.instrument
    108. 108. How ? (2)• Use custom ClassLoader – Override ClassLoader#findClass – Use ClassReader(String) to read the class in and transform it via visitor chain – Call ClassLoader#defineClass explicitly with the result from the transformation step
    109. 109. java.lang.instrumentimport java.lang.instrument.ClassFileTransformer;import java.lang.instrument.Instrumentation;public class Agent {public static void premain(String args, Instrumentation inst) { inst.addTransformer(new ClassFileTransformer(), true); }public static void agentmain(String args, Instrumentation inst) { premain(args,inst); }}
    110. 110. java.lang.instrumentimport java.lang.instrument.ClassFileTransformer;import java.lang.instrument.Instrumentation;public class Agent {public static void premain(String args, Instrumentation inst) { inst.addTransformer(new ClassFileTransformer(), true); }public static void agentmain(String args, Instrumentation inst) { premain(args,inst); }}
    111. 111. java.lang.instrumentimport java.lang.instrument.ClassFileTransformer;import java.lang.instrument.Instrumentation;public class Agent {public static void premain(String args, Instrumentation inst) { inst.addTransformer(new ClassFileTransformer(), true); }public static void agentmain(String args, Instrumentation inst) { premain(args,inst); }} META-INF/MANIFEST.MF Premain-Class: Agent java –javaagent:agent.jar … Agent-Class: Agent
    112. 112. j.l.instrument.ClassFileTransformernew ClassFileTransformer() { public byte[] transform(ClassLoader loader, String className, Class<?>classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer){ ClassReader cr = new ClassReader(classfileBuffer); ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); MyAdapter ca = new MyAdapter(cw); cr.accept(ca, ClassReader.EXPAND_FRAMES); return cw.toByteArray();}
    113. 113. j.l.instrument.ClassFileTransformernew ClassFileTransformer() { public byte[] transform(ClassLoader loader, String className, Class<?>classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer){ ClassReader cr = new ClassReader(classfileBuffer); ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); MyAdapter ca = new MyAdapter(cw); cr.accept(ca, ClassReader.EXPAND_FRAMES); return cw.toByteArray();}
    114. 114. j.l.instrument.ClassFileTransformernew ClassFileTransformer() { public byte[] transform(ClassLoader loader, String className, Class<?>classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer){ ClassReader cr = new ClassReader(classfileBuffer); ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); MyAdapter ca = new MyAdapter(cw); cr.accept(ca, ClassReader.EXPAND_FRAMES); return cw.toByteArray();}
    115. 115. public class MyClassLoader extends ClassLoader { protected Class findClass(String name) throws ClassNotFoundException { ClassReader cr = new ClassReader(name); ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); MyClassAdapter ca = new MyClassAdapter(cw); cr.accept(ca, ClassReader.EXPAND_FRAMES); byte b[] = cw.toByteArray(); return defineClass(name, b, 0, b.length); }
    116. 116. SLIDESGOTO: IDESLIDESIDE: ASM DEMO
    117. 117. @antonarhipov anton@zeroturnaround.comhttps://github.com/antonarhipov/asmdemo
    1. A particular slide catching your eye?

      Clipping is a handy way to collect important slides you want to go back to later.

    ×