binary patching for fun and profit @ jug.ru, 25.02.2012
DESCRIPTION
TRANSCRIPT
Binary Patching for Fun and Profit
withJRebel SDK
Binary Patching
1010101010111000101010101010100010001000111011011101011
1010101010111100001010101010100010001000111011011101110
Ninja.class Ninja.class’
a.k.a instrumentation
Binary Patching
MyClass.class
New code:10010100100100101011001010
ClassLoader
MyObject
Transformer
Ap
plicati
on
Why?
• Programming model (AOP, ORM)• Tooling (profilers, coverage)• Legacy integration
… or maybe you’re just bored?
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
java.lang.instrument
import java.lang.instrument.ClassFileTransformer;import java.lang.instrument.Instrumentation;
public class Agent { public static void premain(String args, Instrumentation inst) throws Exception { inst.addTransformer(new ClassFileTransformer { … }); }}
java.lang.instrument
import java.lang.instrument.ClassFileTransformer;import java.lang.instrument.Instrumentation;
public class Agent { public static void premain(String args, Instrumentation inst) throws Exception { inst.addTransformer(new ClassFileTransformer { … }); }}
java.lang.instrument
import java.lang.instrument.ClassFileTransformer;import java.lang.instrument.Instrumentation;
public class Agent { public static void premain(String args, Instrumentation inst) throws Exception { inst.addTransformer(new ClassFileTransformer { … }); }}
META-INF/MANIFEST.MFPremain-Class: Agent
java –javaagent:agent.jar …
j.l.instrument.ClassFileTransformernew ClassFileTransformer() { public byte[] transform(ClassLoader loader, String className, Class<?>classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer){
ClassPool cp = ClassPool.getDefault(); CtClass ct = cp.makeClass(new ByteArrayInputStream(classfileBuffer)); transformClass(ct, cp); return ct.toBytecode(); }}
j.l.instrument.ClassFileTransformernew ClassFileTransformer() { public byte[] transform(ClassLoader loader, String className, Class<?>classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer){
ClassPool cp = ClassPool.getDefault(); CtClass ct = cp.makeClass(new ByteArrayInputStream(classfileBuffer)); transformClass(ct, cp); return ct.toBytecode(); }}
j.l.instrument.ClassFileTransformernew ClassFileTransformer() { public byte[] transform(ClassLoader loader, String className, Class<?>classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer){
ClassPool cp = ClassPool.getDefault(); CtClass ct = cp.makeClass(new ByteArrayInputStream(classfileBuffer)); transformClass(ct, cp); return ct.toBytecode(); }}
j.l.instrument.ClassFileTransformernew ClassFileTransformer() { public byte[] transform(ClassLoader loader, String className, Class<?>classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer){
ClassPool cp = ClassPool.getDefault(); CtClass ct = cp.makeClass(new ByteArrayInputStream(classfileBuffer)); transformClass(ct, cp); return ct.toBytecode(); }}
Javassist1-2-3
Javassist
• Bytecode manipulation made easy• Source-level and bytecode-level API• Uses the vocabulary of Java language• On-the-fly compilation of the injected code• http://www.jboss.org/javassist
Adding InterfacesClassPool cp = ClassPool.getDefault();
CtClass ct = cp.get("org.geecon.Alarm");
ct.addInterface(cp.get(Listener.class.getName()));
ct.addMethod(CtNewMethod.make("public void fire(){ alert(); }", ct));
public interface Listener {
void fire();
}
public class Alarm {
void alert() {}
}
Adding InterfacesClassPool cp = ClassPool.getDefault();
CtClass ct = cp.get("org.geecon.Alarm");
ct.addInterface(cp.get(Listener.class.getName()));
ct.addMethod(CtNewMethod.make("public void fire(){ alert(); }", ct));
public interface Listener {
void fire();
}
public class Alarm {
void alert() {}
}
Adding InterfacesClassPool cp = ClassPool.getDefault();
CtClass ct = cp.get("org.geecon.Alarm");
ct.addInterface(cp.get(Listener.class.getName()));
ct.addMethod(CtNewMethod.make("public void fire(){ alert(); }", ct));
public interface Listener {
void fire();
}
public class Alarm {
void alert() {}
}
Adding InterfacesClassPool cp = ClassPool.getDefault();
CtClass ct = cp.get("org.geecon.Alarm");
ct.addInterface(cp.get(Listener.class.getName()));
ct.addMethod(CtNewMethod.make("public void fire(){ alert(); }", ct));
public interface Listener {
void fire();
}
public class Alarm {
void alert() {}
}
Simple AOPProxyFactory pf = new ProxyFactory();
pf.setSuperclass(Notifier.class);
pf.setFilter(new MethodFilter() { … });Notifier notifier = (Notifier) pf.createClass().newInstance();
((ProxyObject) notifier).setHandler(new MethodHandler() { … });
System.out.println("calling on()");notifier.on();
System.out.println("calling off()");notifier.off();
public class Notifier {
public void on(){ }
@Pointcut public void off(){}}
Intercept Statements ClassPool pool = ClassPool.getDefault();
CtClass ct = pool.get("org.geecon.PaymentMachine");
ct.getDeclaredMethod("process") .instrument(new ExprEditor() { public void edit(NewExpr e) throws CannotCompileException { e.replace("$_ = $proceed($$);"); } });
JRebel SDKÖ_õ
IDEs Containers Frameworks
Build Tools
More at http://www.jrebel.com/features