KEMBAR78
Mastering java bytecode with ASM - GeeCON 2012 | PPTX
Mastering Java Bytecode with ASM
Learn some bytecode to yourself!
whoami
Anton Arhipov
Java Dev / Product Lead
ZeroTurnaround, JRebel
Messing with bytecode since 2010


anton@zeroturnaround.com
@antonarhipov @javarebel
whoami
Anton Arhipov
Java Dev / Product Lead
ZeroTurnaround, JRebel
Messing with bytecode since 2010


anton@zeroturnaround.com
@antonarhipov @javarebel
Why Bytecode?
•   Know your platform!
•   Build your own JVM language?
•   Programming models (AOP, ORM)
•   Awesome tools (like JRebel )

... just bored?
Bytecode 101     Instrumentation API

       javap   ObjectWeb ASM
Bytecode 101
  Gentle introduction
Adding Two Values




A+B
Adding Two Values




A+B
AB+
Adding Two Values




A+B
AB+
Adding Two Values




A+B   PUSH A
                      A
AB+
Adding Two Values




A+B   PUSH 1
      PUSH 2
                      B
AB+                   A
Adding Two Values




A+B   PUSH 1
      PUSH 2
                      15
AB+   ADD
Adding Two Values




A+B   ICONST_1
      ICONST_2
                      15
AB+   IADD
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)
Model of
Execution
Enter JVM




JVM process
Enter Threads




Thread A    Thread B   Thread C   Thread D
Enter Frames
The Frame
Local variables
0 1 2        …    N
Operand stack

                  #1
                        Constant
                          Pool
Juggling The Stack
Juggling The Stack

dup                      A
pop                      B
swap
dup_x1
dup2_x1
Juggling The Stack

dup                      A
pop                      A
swap                     B
dup_x1
dup2_x1
Juggling The Stack

dup                      A
pop                      B
swap
dup_x1
dup2_x1
Juggling The Stack

dup                      B
pop                      A
swap
dup_x1
dup2_x1
Juggling The Stack

dup                      B
pop                      A
swap                     B
dup_x1
dup2_x1
Juggling The Stack

dup                      B
pop                      A
swap                     B
dup_x1                   B
dup2_x1                  A
Local Variables
Local Variables                         Stack
var      value                      depth     value

0
                      ldc "Hello"    0
                      astore_0
1                                    1
                      iconst_1
2                     astore_1       2

3                     aload_0        3

4                                    4
Local Variables                         Stack
var      value                      depth     value

0
                      ldc "Hello"    0       "Hello"
                      astore_0
1                                    1
                      iconst_1
2                     astore_1       2

3                     aload_0        3

4                                    4
Local Variables                         Stack
var      value                      depth     value

0
                      ldc "Hello"    0
        "Hello"
                      astore_0
1                                    1
                      iconst_1
2                     astore_1       2

3                     aload_0        3

4                                    4
Local Variables                         Stack
var      value                      depth     value

0
                      ldc "Hello"    0
        "Hello"                                     1
                      astore_0
1                                    1
                      iconst_1
2                     astore_1       2

3                     aload_0        3

4                                    4
Local Variables                         Stack
var      value                      depth     value

0
                      ldc "Hello"    0
        "Hello"
                      astore_0
1           1                        1
                      iconst_1
2                     astore_1       2

3                     aload_0        3

4                                    4
Local Variables                         Stack
var      value                      depth     value

0
                      ldc "Hello"    0
        "Hello"                              "Hello"
                      astore_0
1           1                        1
                      iconst_1
2                     astore_1       2

3                     aload_0        3

4                                    4
load
  Local
Variables           Stack
  Table

            store
Method Invoсation
Method Invocation

obj.method(param1, param2);
Method Invocation

obj.method(param1, param2);

      push obj
      push param1
      push param2
      invoke method
Method Invocation

obj.method(param1, param2);
                              obj
      push obj
      push param1
      push param2
      invoke method
Method Invocation

obj.method(param1, param2);
                              param1
      push obj                 obj
      push param1
      push param2
      invoke method
Method Invocation

obj.method(param1, param2);
                              param2
      push obj                param1
      push param1              obj
      push param2
      invoke method
Method Invocation

obj.method(param1, param2);
                              obj?
      push obj
      push param1
      push param2
      invoke method
Operator Overloading
Operator Overloading

[int] A + B    [Foo] A.plus(B)
Operator Overloading

[int] A + B    [Foo] A.plus(B)
     push A       push A
     push B       push B
     iadd         invokevirtual plus
Operator Overloading

[int] A + B    [Foo] A + B
     push A       push A
     push B       push B
     iadd         invokevirtual plus
pop   push




 Stack
pop   push

            load
 Local
Variables            Stack
  Table


            store
pop   push

            load
 Local
Variables            Stack
  Table


            store
pop      push

            load
 Local
Variables                       Stack             invoke
  Table


            store                               pop push


                                 load

                    Local
                   Variables                   Stack
                     Table

                                 store
javap
The disassembler
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 
C:workgeeconclasses>javap Hello -c
C:workgeeconclasses>javap Hello -c
Compiled 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
C:workgeeconclasses>javap Hello -c
Compiled 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
C:workgeeconclasses>javap Hello -c
Compiled 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
C:workgeeconclasses>javap Hello -c
Compiled 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
C:workgeeconclasses>javap Hello -c
Compiled 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
C:workgeeconclasses>javap Hello -c
Compiled 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

public 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
C:workgeeconclasses>javap Hello -c
Compiled 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 field
public 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
C:workgeeconclasses>javap Hello -c
Compiled 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

public 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
C:workgeeconclasses>javap Hello -c
Compiled 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

public 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
C:workgeeconclasses>javap Hello -c
Compiled 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

public 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
What’s #1,#2, etc ?
C:workgeeconclasses>javap Hello -c
Compiled 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

public 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
SLIDES
GOTO: IDE
SLIDES
IDE: JAVAP DEMO
ASM
The de facto standard for
 bytecode manipulation
ASM
• “All purpose bytecode manipulation
  and analysis framework”
• De facto standard bytecode library
• http://asm.ow2.org
Basic Process
• Construct ClassWriter
• Stack up the visitors for:
  • annotations, methods, fields, etc
• Write out bytes
Hello.java
ClassWriter

ClassWriter cw = new ClassWriter(
     ClassWriter.COMPUTE_MAXS |
     ClassWriter.COMPUTE_FRAMES);
COMPUTE_***

• COMPUTE_MAXS
 –ASM will calculate max stack/local vars
• COMPUTE_FRAMES
 –ASM will calculate Java 6 stack map
Visit Class

cv.visit(V1_6,
    ACC_PUBLIC,
    "X",
    null,
    "java/lang/Object",
    null);
Opcodes
• Interface full of constants
  –Bytecodes
  –Visibility modifiers
  –Java versions
  –Other stuff
ACC_***
• Some you know
  –ACC_PUBLIC, ACC_ABSTRACT, etc
• Some you (probably) don’t
  –ACC_BRIDGE, ACC_SYNTHETIC
Class Names


      "java/lang/Object"

packageClass.replaceAll('.', '/')
Type Descriptors
Type Descriptors
  B      byte
  C      char
  S      string
  I      int
  J      long
  F      float
  D      double
  Z      boolean
  V      void
Type Descriptors



Lsome/Class;
Type Descriptors



[Lsome/Class;
Method Signatures

()V                      void foo()

(Ljava/lang/Object;)I    int foo(Object)

([Ljava/lang/String;)V   void main(String[])
Visit Method
MethodVisitor constructor =
  cv.visitMethod(ACC_PUBLIC,
  "<init>",
  "()V",
  null,
  null);
MethodVisitor mv = cv.visitMethod(
    ACC_PUBLIC + ACC_STATIC,
    "main",
    "([Ljava/lang/String;)V",
    null,
    null);
Visit Method
MethodVisitor 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);
Special Methods

• <init>
  –Constructor
• <clinit>
  –Static initializer
MethodVisitor
• Visit annotations
• Visit code
  – Bytecodes, local variables, line numbers, etc
• Visit maxs
  – Pass bogus values if COMPUTE_MAX
Constructor

c.visitVarInsn(ALOAD, 0);
c.visitMethodInsn(INVOKESPECIAL,
   "java/lang/Object", "<init>", "()V");
c.visitInsn(RETURN);
c.visitMaxs(0, 0);
Constructor

c.visitVarInsn(ALOAD, 0);
c.visitMethodInsn(INVOKESPECIAL,
   "java/lang/Object", "<init>", "()V");
c.visitInsn(RETURN);
c.visitMaxs(0, 0);
                           aload_0
                           invokespecial
                           return
public static void main()
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);
public static void main()
mv.visitFieldInsn(GETSTATIC,
      "java/lang/System", "out",
                                          getstatic
      "Ljava/io/PrintStream;");           ldc “Hello”
                                          invokevirtual
mv.visitLdcInsn("Hello");                 return


mv.visitMethodInsn(INVOKEVIRTUAL,
      "java/io/PrintStream", "println",
      "(Ljava/lang/String;)V");

mv.visitInsn(RETURN);
Enter Loops
Enter Loops


start:
  int i = 0
loop:
  print "Hello"
  i=i+1
  if i < 10 goto loop
end: return
Enter Loops


start:
  int i = 0         GOTO isn’t harmful ;)
loop:
  print "Hello"
  i=i+1
  if i < 10 goto loop
end: return
Enter Loops
0:    iconst_0
1:    istore_1
2:    iload_1
3:    bipush    10
5:    if_icmpge 22

System.out.println(“Hello”)

16: iinc 1, 1
19: goto 2
22: return
Enter Loops
start: iconst_0
  1: istore_1
loop: iload_1
  3: bipush     10
  5: if_icmpge end

 System.out.println(“Hello”)

 16: iinc 1, 1
 19: goto loop
end: return
Enter Loops
start: iconst_0
  1: istore_1           int i = 0
loop: iload_1
  3: bipush     10
  5: if_icmpge end

 System.out.println(“Hello”)

 16: iinc 1, 1
 19: goto loop
end: return
Enter Loops
start: iconst_0
  1: istore_1
loop: iload_1
  3: bipush     10      i < 10
  5: if_icmpge end

 System.out.println(“Hello”)

 16: iinc 1, 1
 19: goto loop
end: return
Enter Loops
start: iconst_0
  1: istore_1
loop: iload_1
  3: bipush     10
  5: if_icmpge end

 System.out.println(“Hello”)

 16: iinc 1, 1         i++
 19: goto loop
end: return
Enter Loops
start: iconst_0
  1: istore_1
loop: iload_1
  3: bipush     10
  5: if_icmpge end

 System.out.println(“Hello”)

 16: iinc 1, 1
 19: goto loop
end: return
Enter ASM Loops
Label start = new Label();
Label loop = new Label();
Label end = new Label();

// i = 0
mv.visitLabel(start);
mv.visitInsn(ICONST_0);
mv.visitVarInsn(ISTORE, 1);
Enter ASM Loops
Label start = new Label();
Label loop = new Label();
Label end = new Label();

// i = 0
mv.visitLabel(start);
mv.visitInsn(ICONST_0);
mv.visitVarInsn(ISTORE, 1);

// i < 10
mv.visitLabel(loop);
mv.visitVarInsn(ILOAD, 1);
mv.visitLdcInsn(10);
mv.visitJumpInsn(IF_ICMPGE, end);
Enter ASM Loops
Label 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 < 10
mv.visitLabel(loop);
mv.visitVarInsn(ILOAD, 1);
mv.visitLdcInsn(10);
mv.visitJumpInsn(IF_ICMPGE, end);
ClassWriter
geecon$ java Hello
Hello
Hello
Hello
Hello
Hello
Hello
Hello
Hello
Hello
Hello
ASMified
     ASMifierClassVisitor
mv = 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.class
mv.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();
Bytecode
instrumentation
 Some magic for your own good
WAT!?
Ninja.class           Ninja.class’
10101010101           10101010101
11000101010           11100001010
10101010001           10101010001
00010001110           00010001110
11011101011           11011101110
Who?

          Containers (Java EE, Spring)

                   Terracotta
JRebel

                         Tapestry
         Byteman
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
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
java.lang.instrument

import 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); }
}
java.lang.instrument

import 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); }
}
java.lang.instrument

import 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
j.l.instrument.ClassFileTransformer
new 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();
}
j.l.instrument.ClassFileTransformer
new 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();
}
j.l.instrument.ClassFileTransformer
new 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();
}
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);
 }
SLIDES
GOTO: IDE
SLIDES
IDE: ASM DEMO
@antonarhipov
    anton@zeroturnaround.com
https://github.com/antonarhipov/asmdemo

Mastering java bytecode with ASM - GeeCON 2012

  • 1.
    Mastering Java Bytecodewith ASM Learn some bytecode to yourself!
  • 2.
    whoami Anton Arhipov Java Dev/ Product Lead ZeroTurnaround, JRebel Messing with bytecode since 2010 anton@zeroturnaround.com @antonarhipov @javarebel
  • 3.
    whoami Anton Arhipov Java Dev/ Product Lead ZeroTurnaround, JRebel Messing with bytecode since 2010 anton@zeroturnaround.com @antonarhipov @javarebel
  • 4.
    Why Bytecode? • Know your platform! • Build your own JVM language? • Programming models (AOP, ORM) • Awesome tools (like JRebel ) ... just bored?
  • 5.
    Bytecode 101 Instrumentation API javap ObjectWeb ASM
  • 6.
    Bytecode 101 Gentle introduction
  • 7.
  • 8.
  • 9.
  • 10.
    Adding Two Values A+B PUSH A A AB+
  • 11.
    Adding Two Values A+B PUSH 1 PUSH 2 B AB+ A
  • 12.
    Adding Two Values A+B PUSH 1 PUSH 2 15 AB+ ADD
  • 13.
    Adding Two Values A+B ICONST_1 ICONST_2 15 AB+ IADD
  • 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.
  • 16.
  • 17.
    Enter Threads Thread A Thread B Thread C Thread D
  • 18.
  • 19.
    The Frame Local variables 01 2 … N Operand stack #1 Constant Pool
  • 20.
  • 21.
    Juggling The Stack dup A pop B swap dup_x1 dup2_x1
  • 22.
    Juggling The Stack dup A pop A swap B dup_x1 dup2_x1
  • 23.
    Juggling The Stack dup A pop B swap dup_x1 dup2_x1
  • 24.
    Juggling The Stack dup B pop A swap dup_x1 dup2_x1
  • 25.
    Juggling The Stack dup B pop A swap B dup_x1 dup2_x1
  • 26.
    Juggling The Stack dup B pop A swap B dup_x1 B dup2_x1 A
  • 27.
  • 28.
    Local Variables Stack var value depth value 0 ldc "Hello" 0 astore_0 1 1 iconst_1 2 astore_1 2 3 aload_0 3 4 4
  • 29.
    Local Variables Stack var value depth value 0 ldc "Hello" 0 "Hello" astore_0 1 1 iconst_1 2 astore_1 2 3 aload_0 3 4 4
  • 30.
    Local Variables Stack var value depth value 0 ldc "Hello" 0 "Hello" astore_0 1 1 iconst_1 2 astore_1 2 3 aload_0 3 4 4
  • 31.
    Local Variables Stack var value depth value 0 ldc "Hello" 0 "Hello" 1 astore_0 1 1 iconst_1 2 astore_1 2 3 aload_0 3 4 4
  • 32.
    Local Variables Stack var value depth value 0 ldc "Hello" 0 "Hello" astore_0 1 1 1 iconst_1 2 astore_1 2 3 aload_0 3 4 4
  • 33.
    Local Variables Stack var value depth value 0 ldc "Hello" 0 "Hello" "Hello" astore_0 1 1 1 iconst_1 2 astore_1 2 3 aload_0 3 4 4
  • 34.
    load Local Variables Stack Table store
  • 35.
  • 36.
  • 37.
    Method Invocation obj.method(param1, param2); push obj push param1 push param2 invoke method
  • 38.
    Method Invocation obj.method(param1, param2); obj push obj push param1 push param2 invoke method
  • 39.
    Method Invocation obj.method(param1, param2); param1 push obj obj push param1 push param2 invoke method
  • 40.
    Method Invocation obj.method(param1, param2); param2 push obj param1 push param1 obj push param2 invoke method
  • 41.
    Method Invocation obj.method(param1, param2); obj? push obj push param1 push param2 invoke method
  • 42.
  • 43.
    Operator Overloading [int] A+ B [Foo] A.plus(B)
  • 44.
    Operator Overloading [int] A+ B [Foo] A.plus(B) push A push A push B push B iadd invokevirtual plus
  • 45.
    Operator Overloading [int] A+ B [Foo] A + B push A push A push B push B iadd invokevirtual plus
  • 46.
    pop push Stack
  • 47.
    pop push load Local Variables Stack Table store
  • 48.
    pop push load Local Variables Stack Table store
  • 49.
    pop push load Local Variables Stack invoke Table store pop push load Local Variables Stack Table store
  • 50.
  • 51.
    javap • Java classfile 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 
  • 53.
  • 54.
    C:workgeeconclasses>javap Hello -c Compiledfrom "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
  • 55.
    C:workgeeconclasses>javap Hello -c Compiledfrom "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
  • 56.
    C:workgeeconclasses>javap Hello -c Compiledfrom "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
  • 57.
    C:workgeeconclasses>javap Hello -c Compiledfrom "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
  • 58.
    C:workgeeconclasses>javap Hello -c Compiledfrom "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
  • 59.
    C:workgeeconclasses>javap Hello -c Compiledfrom "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 public 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.
    C:workgeeconclasses>javap Hello -c Compiledfrom "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 field public 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
  • 61.
    C:workgeeconclasses>javap Hello -c Compiledfrom "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 public 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
  • 62.
    C:workgeeconclasses>javap Hello -c Compiledfrom "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 public 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
  • 63.
    C:workgeeconclasses>javap Hello -c Compiledfrom "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 public 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.
    What’s #1,#2, etc? C:workgeeconclasses>javap Hello -c Compiled 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 public 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
  • 65.
  • 66.
    ASM The de factostandard for bytecode manipulation
  • 67.
    ASM • “All purposebytecode manipulation and analysis framework” • De facto standard bytecode library • http://asm.ow2.org
  • 68.
    Basic Process • ConstructClassWriter • Stack up the visitors for: • annotations, methods, fields, etc • Write out bytes
  • 69.
  • 70.
    ClassWriter ClassWriter cw =new ClassWriter( ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
  • 71.
    COMPUTE_*** • COMPUTE_MAXS –ASMwill calculate max stack/local vars • COMPUTE_FRAMES –ASM will calculate Java 6 stack map
  • 72.
    Visit Class cv.visit(V1_6, ACC_PUBLIC, "X", null, "java/lang/Object", null);
  • 73.
    Opcodes • Interface fullof constants –Bytecodes –Visibility modifiers –Java versions –Other stuff
  • 74.
    ACC_*** • Some youknow –ACC_PUBLIC, ACC_ABSTRACT, etc • Some you (probably) don’t –ACC_BRIDGE, ACC_SYNTHETIC
  • 75.
    Class Names "java/lang/Object" packageClass.replaceAll('.', '/')
  • 76.
  • 77.
    Type Descriptors B byte C char S string I int J long F float D double Z boolean V void
  • 78.
  • 79.
  • 80.
    Method Signatures ()V void foo() (Ljava/lang/Object;)I int foo(Object) ([Ljava/lang/String;)V void main(String[])
  • 81.
    Visit Method MethodVisitor constructor= cv.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null); MethodVisitor mv = cv.visitMethod( ACC_PUBLIC + ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null);
  • 82.
    Visit Method MethodVisitor 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);
  • 83.
    Special Methods • <init> –Constructor • <clinit> –Static initializer
  • 84.
    MethodVisitor • Visit annotations •Visit code – Bytecodes, local variables, line numbers, etc • Visit maxs – Pass bogus values if COMPUTE_MAX
  • 85.
    Constructor c.visitVarInsn(ALOAD, 0); c.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V"); c.visitInsn(RETURN); c.visitMaxs(0, 0);
  • 86.
    Constructor c.visitVarInsn(ALOAD, 0); c.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V"); c.visitInsn(RETURN); c.visitMaxs(0, 0); aload_0 invokespecial return
  • 87.
  • 88.
    public static voidmain() 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);
  • 89.
    public static voidmain() mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", getstatic "Ljava/io/PrintStream;"); ldc “Hello” invokevirtual mv.visitLdcInsn("Hello"); return mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V"); mv.visitInsn(RETURN);
  • 90.
  • 91.
    Enter Loops start: int i = 0 loop: print "Hello" i=i+1 if i < 10 goto loop end: return
  • 92.
    Enter Loops start: int i = 0 GOTO isn’t harmful ;) loop: print "Hello" i=i+1 if i < 10 goto loop end: return
  • 93.
    Enter Loops 0: iconst_0 1: istore_1 2: iload_1 3: bipush 10 5: if_icmpge 22 System.out.println(“Hello”) 16: iinc 1, 1 19: goto 2 22: return
  • 94.
    Enter Loops start: iconst_0 1: istore_1 loop: iload_1 3: bipush 10 5: if_icmpge end System.out.println(“Hello”) 16: iinc 1, 1 19: goto loop end: return
  • 95.
    Enter Loops start: iconst_0 1: istore_1 int i = 0 loop: iload_1 3: bipush 10 5: if_icmpge end System.out.println(“Hello”) 16: iinc 1, 1 19: goto loop end: return
  • 96.
    Enter Loops start: iconst_0 1: istore_1 loop: iload_1 3: bipush 10 i < 10 5: if_icmpge end System.out.println(“Hello”) 16: iinc 1, 1 19: goto loop end: return
  • 97.
    Enter Loops start: iconst_0 1: istore_1 loop: iload_1 3: bipush 10 5: if_icmpge end System.out.println(“Hello”) 16: iinc 1, 1 i++ 19: goto loop end: return
  • 98.
    Enter Loops start: iconst_0 1: istore_1 loop: iload_1 3: bipush 10 5: if_icmpge end System.out.println(“Hello”) 16: iinc 1, 1 19: goto loop end: return
  • 99.
    Enter ASM Loops Labelstart = new Label(); Label loop = new Label(); Label end = new Label(); // i = 0 mv.visitLabel(start); mv.visitInsn(ICONST_0); mv.visitVarInsn(ISTORE, 1);
  • 100.
    Enter ASM Loops Labelstart = new Label(); Label loop = new Label(); Label end = new Label(); // i = 0 mv.visitLabel(start); mv.visitInsn(ICONST_0); mv.visitVarInsn(ISTORE, 1); // i < 10 mv.visitLabel(loop); mv.visitVarInsn(ILOAD, 1); mv.visitLdcInsn(10); mv.visitJumpInsn(IF_ICMPGE, end);
  • 101.
    Enter ASM Loops Labelstart = 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 < 10 mv.visitLabel(loop); mv.visitVarInsn(ILOAD, 1); mv.visitLdcInsn(10); mv.visitJumpInsn(IF_ICMPGE, end);
  • 103.
  • 104.
  • 105.
    ASMified ASMifierClassVisitor mv = 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.class mv.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();
  • 106.
  • 107.
    WAT!? Ninja.class Ninja.class’ 10101010101 10101010101 11000101010 11100001010 10101010001 10101010001 00010001110 00010001110 11011101011 11011101110
  • 108.
    Who? Containers (Java EE, Spring) Terracotta JRebel Tapestry Byteman
  • 109.
    How? • Add –javaagentto hook into class loading process • Implement ClassFileTransformer • Use bytecode manipulation libraries (Javassist, cglib, asm) to add any custom logic java.lang.instrument
  • 110.
    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
  • 111.
    java.lang.instrument import java.lang.instrument.ClassFileTransformer; import java.lang.instrument.Instrumentation; publicclass 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); } }
  • 112.
    java.lang.instrument import java.lang.instrument.ClassFileTransformer; import java.lang.instrument.Instrumentation; publicclass 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); } }
  • 113.
    java.lang.instrument import java.lang.instrument.ClassFileTransformer; import java.lang.instrument.Instrumentation; publicclass 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
  • 114.
    j.l.instrument.ClassFileTransformer new 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.
    j.l.instrument.ClassFileTransformer new 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(); }
  • 116.
    j.l.instrument.ClassFileTransformer new 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(); }
  • 117.
    public class MyClassLoaderextends 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); }
  • 118.
  • 119.
    @antonarhipov anton@zeroturnaround.com https://github.com/antonarhipov/asmdemo

Editor's Notes

  • #5 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 
  • #6 This presentation provides some pointers to the subject
  • #51 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.
  • #69 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.
  • #71 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
  • #106 ... using ASMifierClassVisitor (ASMifier in ASM4). The output is rather noisy, with all the labels,but the noise is easy to remove.