Download Mastering Java Bytecode with ASM

Document related concepts
no text concepts found
Transcript
Mastering Java Bytecode
with
Learn
someASM
bytecode to yourself!
whoami
Anton Arhipov
Java Dev / Product Lead
ZeroTurnaround, JRebel
Messing with bytecode since 2010
[email protected]
@antonarhipov @javarebel
whoami
Anton Arhipov
Java Dev / Product Lead
ZeroTurnaround, JRebel
Messing with bytecode since 2010
[email protected]
@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 AP
ObjectWeb
ASM
javap
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
AB+
PUSH A
A
Adding Two Values
A+B
AB+
PUSH 1
PUSH 2
B
A
Adding Two Values
A+B
AB+
PUSH 1
PUSH 2
ADD
15
Adding Two Values
A+B
AB+
ICONST_1
ICONST_2
IADD
15
OPERATIO
TYPE
N
• <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
Threa
dA
Threa
dB
Threa
dC
Threa
dD
Enter Frames
The Frame
Local variables
012 … N
Operand stack
#
1
Constan
t
Pool
Juggling The Stack
Juggling The Stack
dup
pop
swap
dup_x1
dup2_x
1
A
B
Juggling The Stack
dup
pop
swap
dup_x1
dup2_x
1
A
A
B
Juggling The Stack
dup
pop
swap
dup_x1
dup2_x
1
A
B
Juggling The Stack
dup
pop
swap
dup_x1
dup2_x
1
B
A
Juggling The Stack
dup
pop
swap
dup_x1
dup2_x
1
B
A
B
Juggling The Stack
dup
pop
swap
dup_x1
dup2_x
1
B
A
B
B
A
Local Variables
Local
va Variables
valu
r
0
1
2
3
4
e
Stack
ldc
"Hello"
astore_0
iconst_1
astore_1
aload_0
dept
h
0
1
2
3
4
valu
e
Local
va Variables
valu
r
0
1
2
3
4
e
Stack
ldc
"Hello"
astore_0
iconst_1
astore_1
aload_0
dept
h
0
1
2
3
4
valu
e
"Hello
"
Local
va Variables
valu
r
0
1
2
3
4
e
"Hello
"
Stack
ldc
"Hello"
astore_0
iconst_1
astore_1
aload_0
dept
h
0
1
2
3
4
valu
e
Local
va Variables
valu
r
0
1
2
3
4
e
"Hello
"
Stack
ldc
"Hello"
astore_0
iconst_1
astore_1
aload_0
dept
h
0
1
2
3
4
valu
e
1
Local
va Variables
valu
r
0
1
2
3
4
e
"Hello
"
1
Stack
ldc
"Hello"
astore_0
iconst_1
astore_1
aload_0
dept
h
0
1
2
3
4
valu
e
Local
va Variables
valu
r
0
1
2
3
4
e
"Hello
"
1
Stack
ldc
"Hello"
astore_0
iconst_1
astore_1
aload_0
dept
h
0
1
2
3
4
valu
e
"Hello
"
load
Local
Variable
s Table
Stack
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);
push obj
push param1
push param2
invoke
method
obj
Method Invocation
obj.method(param1,
param2);
push obj
push param1
push param2
invoke
method
param
1
obj
Method Invocation
obj.method(param1,
param2);
push obj
push param1
push param2
invoke
method
param
2
param
1
obj
Method Invocation
obj.method(param1,
param2);
push obj
push param1
push param2
invoke
method
obj?
Operator Overloading
Operator Overloading
[int] A +
B
[Foo] A.plus(
B)
Operator Overloading
[int] A +
B
push
A
push
B
iadd
[Foo] A.plus(
B)
push A
push B
invokevirtual
plus
Operator Overloading
[int] A +
B
push
A
push
B
iadd
[Foo] A + B
push A
push B
invokevirtual
plus
pop push
Stack
pop push
load
Local
Variable
s
Table
Stack
store
pop push
load
Local
Variable
s
Table
Stack
store
pop push
load
Local
Variable
s
Table
Stack
store
invoke
pop push
load
Local
Variable
s
Table
Stack
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:\work\geecon\classes>javap Hello -c
C:\work\geecon\classes>javap Hello -c
Compiled from "Hello.java"
public class Hello extends java.lang.Object{
public Hello();
the default constructor
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":
()V
4: return
C:\work\geecon\classes>javap Hello -c
Compiled from "Hello.java"
public class Hello extends java.lang.Object{
public Hello();
push this to stack
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":
()V
4: return
C:\work\geecon\classes>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:\work\geecon\classes>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>":
super()
()V
4: return
C:\work\geecon\classes>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:\work\geecon\classes>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:
C:\work\geecon\classes>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:
C:\work\geecon\classes>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!
load string to the stack
5: invokevirtual #4; //Method java/io/PrintStream.println:
C:\work\geecon\classes>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!
invoke
method with parameter
5: invokevirtual #4; //Method java/io/PrintStream.println:
C:\work\geecon\classes>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:
What’s #1,#2, etc
C:\work\geecon\classes>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:
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
C
S
I
J
F
D
Z
V
byte
char
string
int
long
float
double
boolean
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");
aload_0
c.visitInsn(RETURN);
invokespeci
c.visitMaxs(0, 0); al
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",
"Ljava/io/PrintStream;");
mv.visitLdcInsn("Hello");
getstatic
ldc
“Hello”
invokevirt
ual
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:
1:
2:
3:
5:
iconst_0
istore_1
iload_1
bipush10
if_icmpge 22
System.out.println(“Hell
o”)
16: iinc 1, 1
Enter Loops
start: iconst_0
1: istore_1
loop: iload_1
3: bipush10
5: if_icmpge end
System.out.println(“Hell
o”)
16: iinc 1, 1
Enter Loops
start: iconst_0
int i = 0
1: istore_1
loop: iload_1
3: bipush10
5: if_icmpge end
System.out.println(“Hell
o”)
16: iinc 1, 1
Enter Loops
start: iconst_0
1: istore_1
loop: iload_1
3: bipush10
i < 10
5: if_icmpge end
System.out.println(“Hell
o”)
16: iinc 1, 1
Enter Loops
start: iconst_0
1: istore_1
loop: iload_1
3: bipush10
5: if_icmpge end
System.out.println(“Hell
o”)
i++
16: iinc 1, 1
Enter Loops
start: iconst_0
1: istore_1
loop: iload_1
3: bipush10
5: if_icmpge end
System.out.println(“Hell
o”)
16: iinc 1, 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);
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
// i = 0
mv.visitIincInsn(1, 1);
mv.visitLabel(start);
mv.visitJumpInsn(GOTO,
mv.visitInsn(ICONST_0); loop);
mv.visitVarInsn(ISTORE, 1);mv.visitLabel(end);
// 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
ASMifierClassVis
itor
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",
a"Ljava/util/List;");
-cp asm-all-3.3.1.jar:asm-util-3.3.1.ja
mv.visitVarInsn(ILOAD, 1);
.objectweb.asm.util.ASMifierClassVisitor
mv.visitMethodInsn(INVOKEINTERFACE, "java/util/List",
lo.class
"get",
"(I)Ljava/lang/Object;");
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);
Bytecode instrumentation
Some magic for your own
good
WAT!?
Ninja.clas
s
101010101
011100010
101010101
010001000
100011101
101110101
1
Ninja.clas
s’
101010101
011110000
101010101
010001000
100011101
101110111
0
Who?
tJ
AspecContainers
(Java EE, Spri
Play! Fra
mework Terracotta
JRebel
Hibernate
s
g
u
B
d
Fin
Byteman
Tapestr
y
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.instrume
nt
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)
META-INF/MANIFEST.MF
java –javaagent:agent.jar
{ Premain-Class:
premain(args,inst);
}
Agent
…
} Agent-Class: Agent
j.l.instrument.ClassFileTransforme
r
new ClassFileTransformer () {
public byte[] transform(ClassLoader loader, String clas
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.ClassFileTransforme
r
new ClassFileTransformer () {
public byte[] transform(ClassLoader loader, String clas
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.ClassFileTransforme
r
new ClassFileTransformer () {
public byte[] transform(ClassLoader loader, String clas
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);
SLIDES
GOTO: IDE
SLIDES
IDE: ASM DEMO
@antonarhipov
[email protected]
https://github.com/antonarhipov/
Related documents