Michael Rasmussen ZeroTurnaround  Terminology

 Basic ASM classes

 Generating

 Transforming bytecode

 Bytecode in the Wild

 Binary names ◦ In Java source code file  Uses dots to separate, e.g. java.lang.String  In accordance with the JLS  http://docs.oracle.com/javase/specs/jls/se7/html/jls-13.html#jls-13.1

◦ In class file format  Uses slash to separate, e.g. java/lang/String  Also commonly referred to as Internal name or Internal form  In accordance with the JVMS  http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.2  String representing a class’ name

 Fully qualified class name ◦ uses / as package separator

 Example: ◦ java.lang.String “java/lang/String” ◦ java.util.ArrayList “java/util/ArrayList”  Internal definition of a type

 Primitive types ◦ Single character  “J” (long), “I” (int), “S” (short), “B” (byte), “C” (char)  “F" (float), “D” (double)  “Z” (boolean), “V” (void)  Object types ◦ Binary name enclosed by L;  “Ljava/lang/String;”  “Ljava/util/ArrayList;”

 Array types ◦ [ followed by type descriptor  “[B” byte[]  “[Ljava/lang/String;” String[]  “[[D” double[][]  Multiple [ indicates multiple dimensions  Defines parameter and return types

 Consists of: ◦ Enclosed in parenthesis: 0 or more Type Descriptors  Describing the parameters ◦ 1 Type Descriptor  Describing the return type  Example ◦ “(Ljava/lang/String;)I”  Takes a java.lang.String as arguments  Returns an int ◦ “()V”  Takes zero arguments  Returns nothing (void) ◦ “(DD)D”  Takes two arguments of type double  Returns a double  Constructor ◦ Special name: “” ◦ Must invoke constructor on superclass (or another constructor on this class)

 Static initializer ◦ Special name: “” ◦ Static method invoked when the class is initialized  Ensured to only be called once  Ensured to have been called before any methods on the class is called  Bit-mask containing access flags ◦ ACC_PUBLIC class, field, method ◦ ACC_PRIVATE class, field, method ◦ ACC_PROTECTED class, field, method ◦ ACC_STATIC field, method ◦ ACC_FINAL class, field, method, parameter ◦ ACC_SUPER class ◦ ACC_SYNCHRONIZED method ◦ ACC_VOLATILE field ◦ ACC_BRIDGE method ◦ ACC_VARARGS method ◦ ACC_TRANSIENT field ◦ ACC_NATIVE method ◦ ACC_INTERFACE class ◦ ACC_ABSTRACT class, method ◦ ACC_STRICT method ◦ ACC_SYNTHETIC class, field, method, parameter ◦ ACC_ANNOTATION class ◦ ACC_ENUM class, field ◦ ACC_MANDATED parameter Compiled from "Test.java” public class Test { public Test(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."":()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 8: return }

 Maven dependency: org.ow2.asm asm-debug-all 5.2

 Gradle dependency:

compile 'org.ow2.asm:asm-debug-all:5.2'  ASM is based on the Visitor pattern

 ClassVisitor – visit Class members ◦ ClassWriter – write .class bytes ◦ ClassReader – read .class bytes

 MethodVisitor – visit Method bytecode ◦ GeneratorAdapter – wrapper to ease generation

 Label - Branch target  Type - Helper class for types  Base Visitor to visit a class ◦ Contains visitors for (among others):  Describing the class, superclass, interfaces  Visiting fields  Visiting methods  visit(...) ◦ Visits the overall class  int version Java version  int access Access for class (ACC_PUBLIC)  String name Name of class  String signature Generic signature  String superName Name of superclass  String[] interfaces Names of implemented interfaces  visitField(…) : FieldVisitor ◦ Visit a field on the class  int access Access for the field (public/private)  String name Name of the field  String desc Type descriptor of the field  String signature Generic signature for the field  Object value Default value (for static primitives)  visitMethod(…) : MethodVisitor ◦ Visit a method on the class  int access Access for the method  String name Name of the method  String desc Method descriptor of the method  String signature Generic signature for the method  String[] exceptions Declared thrown exceptions  Extends ClassVisitor

 Basic class to generate a class

 Use toByteArray() to get actual bytecode  Basic class to parse a class

 Use accept() with a ClassVisitor ◦ visits on the passed ClassVisitor the individual parts of the class being parsed  Base Visitor to visit a method

 Has visit methods for individual bytecode opcode-groups  visitInsn(int opcode) ◦ Visits a zero operand instruction  xRETURN, ATHROW  xCONST_n  xALOAD, xASTORE, ARRAYLENGTH  xADD, xSUB, xMUL, xDIV, xREM, xNEG  xOR, xAND, xXOR, xSHL, xSHR, xUSHR  I2x, L2x, F2x, D2x  LCMP, FCMPx, DCMPx  NOP, SWAP, DUP, DUP2, POP, POP2, DUP_x  MONITORENTER, MONITOREXIT  visitIntInsn(int opcode, int operand) ◦ Visits an instruction with a single int operand  BIPUSH, SIPUSH  Pushed the value “operand” onto the stack  NEWARRAY  Creates a primitive array of the type specified by “operand”:  T_BOOLEAN, T_CHAR, T_FLOAT, T_DOUBLE, T_BYTE, T_SHORT, T_INT, T_LONG  Size of array is popped from the stack  visitVarInsn(int opcode, int var) ◦ Visits a local instruction  xLOAD  xSTORE

◦ “var” indicates the local index to access  visitTypeInsn(int opcode, String type) ◦ Visits a type instruction.  NEW  ANEWARRAY  CHECKCAST  INSTANCEOF

◦ “type” is the type’s binary name  visitFieldInsn(int opcode, String owner, String name, String desc) ◦ Visits a field instruction  GETSTATIC, PUTSTATIC  GETFIELD, PUTFIELD

◦ “owner” is the binary-name of the class that holds the field ◦ “name” is the name of the field ◦ “desc” is the type-descriptor for the field  visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) ◦ Visits a method instruction  INVOKESTATIC  INVOKEVIRTUAL, INVOKESPECIAL, INVOKEINTERFACE

◦ “owner” is the binary-name of the class that holds the method ◦ “name” is the name of the method ◦ “desc” is the method-descriptor of the method ◦ “itf” indicates if it’s an interface-method  visitJumpInsn(int opcode, Label label) ◦ Visits a jump instruction.  GOTO, IF_ICMPx, IFx, IF_ACMPx, etc

◦ “label” points to the target location to jump to if condition is met  visitLdcInsn(Object cst) ◦ Visits an LDC instruction.  LDC

◦ If “cst” is of the type Integer, Long, Float or Double, the value of that number is used as a constant. ◦ If “cst” is of type String, the content of that String is used as a constant. ◦ If “cst” is of type Type, the type indicated by that Type is used as a constant (equivalent to using .class in Java source code)  visitIincInsn(int var, int increment) ◦ Visits an IINC instruction.  IINC

◦ “var” points to the local index to modify ◦ “increment” is the value the local should be incremented by (valid range: -32768 .. 32767)  visitLabel(Label label) ◦ Visits a label.

◦ Adds the target location “label”, for use with, among others, jump opcodes.  Wrapper around MethodVisitor ◦ Convenience methods for most opcodes

◦ Easier to write

◦ Takes care of long/double taking two slots!  Access arguments by index, not by local

◦ Takes care of using the right opcode depending on type  Symbolizes branch targets in methods

◦ Example:  Label labelGoto = new Label(); mv.visitJumpInsn(GOTO, labelGoto); mv.visitLabel(labelGoto);  ASM class for type/method descriptors ◦ Contains several useful helper methods  Type.getInternalName(Class c) : String  Returns the binary-name for a class  Type.getDescriptor(Class c) : String  Returns the type-description for a class  Type.getType(Class c) : Type  Return a Type object for the specified class  Type.getMethodDescriptor(Type returnType, Type... paramTypes) : String  Returns the method-descriptor for the types specified

 Basics for generating a class ◦ ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);

cw.visit(V1_6, ACC_PUBLIC, "className", null, Type.getInternalName(Object.class), null);

// Visit individual fields and methods // cw.visitField(...) // cw.visitMethod(...)

cw.visitEnd();

byte[] classBytes = cw.toByteArray(); // define classBytes using a ClassLoader  public static void main(String[] args) { System.out.println("Hello World"); }  Resulting bytecode: ◦ GETSTATIC // Get the static variable System.out java/lang/System out Ljava/io/PrintStream; // which is of the type PrintStream LDC "Hello World" // Push “Hello World” to the stack INVOKEVIRTUAL java/io/PrintStream // invoke println on PrintStream println // with the pushed string (Ljava/lang/String;)V RETURN // All methods must return, // even void methods

public static void main(String[] args) { System.out.println("Hello World"); }  Basics for generating a method ◦ MethodVisitor mv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "main", Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(String[].class)), null, null); mv.visitCode();

mv.visitFieldInsn(GETSTATIC, Type.getInternalName(System.class), "out", Type.getDescriptor(PrintStream.class));

mv.visitLdcInsn("Hello World!");

mv.visitMethodInsn(INVOKEVIRTUAL, Type.getInternalName(PrintStream.class), "println”, Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(String.class)), false);

mv.visitInsn(RETURN);

mv.visitMaxs(2, 1); mv.visitEnd();

public static void main(String[] args) { System.out.println("Hello World"); }  GeneratorAdapter ◦ MethodVisitor mv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "main", Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(String[].class)), null, null); GeneratorAdapter ga = new GeneratorAdapter(mv, ACC_PUBLIC | ACC_STATIC, "main", Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(String[].class)));

ga.getStatic(Type.getType(System.class), "out", Type.getType(PrintStream.class));

ga.push("Hello World");

ga.invokeVirtual(Type.getType(PrintStream.class), Method.getMethod("void println(java.lang.String)"));

ga.returnValue(); ga.endMethod();

public static void main(String[] args) { System.out.println("Hello World"); }  public int gt(int x, int y) { if (x > y) return 1; else return -1; }  Resulting bytecode: ◦ ILOAD 1 // push local[1] {x} ILOAD 2 // push local[2] {y} IF_ICMPLE :le // if local[1] <= local[2] jump ICONST_1 // push +1 IRETURN // return value :le ICONST_M1 // push -1 IRETURN // return value

public int gt(int x, int y) { if (x > y) return 1; else return -1; }  GeneratorAdapter ◦ MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "gt", Type.getMethodDescriptor(Type.INT_TYPE, Type.INT_TYPE, Type.INT_TYPE), null, null); mv.visitCode();

Label labelElse = new Label(); mv.visitVarInst(ILOAD, 1); mv.visitVarInst(ILOAD, 2); mv.visitJumpInst(IF_ICMPLE, labelElse);

mv.visitInst(ICONST_1); mv.visitInst(IRETURN);

mv.visitLabel(labelElse); mv.visitInst(ICONST_M1); mv.visitInst(IRETURN);

mv.visitMaxs(2, 3); mv.visitEnd();

public int gt(int x, int y) { if (x > y) return 1; else return -1; }  GeneratorAdapter ◦ MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "gt", Type.getMethodDescriptor(Type.INT_TYPE, Type.INT_TYPE, Type.INT_TYPE), null, null); GeneratorAdapter ga = new GeneratorAdapter(mv, ACC_PUBLIC, "gt", Type.getMethodDescriptor(Type.INT_TYPE, Type.INT_TYPE, Type.INT_TYPE));

Label labelElse = ga.newLabel(); ga.loadArg(0); ga.loadArg(1); ga.ifICmp(LE, labelElse);

ga.push(1); ga.returnValue();

ga.visitLabel(labelElse); ga.push(-1); ga.returnValue();

ga.endMethod();

public int gt(int x, int y) { if (x > y) return 1; else return -1; }  Basic loop example:

◦ public static void count() { for (int i = 0; i < 10; i++) { System.out.println(i); } }  Basic loop example: ◦ MethodVisitor mv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "count", Type.getMethodDescriptor(Type.VOID_TYPE), null, null); Label labelLoop = new Label(); Label labelEnd = new Label(); mv.visitCode(); mv.visitInsn(ICONST_0); mv.visitVarInsn(ISTORE, 1); mv.visitLabel(labelLoop); mv.visitVarInsn(ILOAD, 1); mv.visitIntInsn(BIPUSH, 10); mv.visitJumpInsn(IF_ICMPGE, labelEnd); mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); mv.visitVarInsn(ILOAD, 1); mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(I)V", false); mv.visitIincInsn(1, 1); mv.visitJumpInsn(GOTO, labelLoop); mv.visitLabel(labelEnd); mv.visitInst(RETURN); mv.visitMaxs(2, 1); mv.visitEnd(); public static void count() { for (int i = 0; i < 10; i++) { System.out.println(i); } }  Basic loop example: ◦ MethodVisitor mv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "count", Type.getMethodDescriptor(Type.VOID_TYPE), null, null); GeneratorAdapter ga = new GeneratorAdapter(mv, ACC_PUBLIC | ACC_STATIC, "count", Type.getMethodDescriptor(Type.VOID_TYPE));

int localI = ga.newLocal(Type.INT_TYPE); Label labelLoop = ga.newLabel(); Label labelEnd = ga.newLabel(); ga.push(0); ga.storeLocal(localI); ga.visitLabel(labelLoop); ga.loadLocal(localI); ga.push(10); ga.ifICmp(GeneratorAdapter.GE, labelEnd); ga.getStatic(Type.getType(System.class), "out", Type.getType(PrintStream.class)); ga.loadLocal(localI); ga.invokeVirtual(Type.getType(PrintStream.class), Method.getMethod("void println(int)")); ga.iinc(localI, 1); ga.goTo(labelLoop); ga.visitLabel(labelEnd); ga.returnValue();

ga.endMethod(); public static void count() { for (int i = 0; i < 10; i++) { System.out.println(i); } }  mv = cw.visitMethod(ACC_PUBLIC, "calc", "(II)I", null, null); Label labelStart = new Label(); Label labelEnd = new Label(); mv.visitCode(); mv.visitInsn(ICONST_1); mv.visitVarInsn(ISTORE, 3); mv.visitInsn(ICONST_0); mv.visitVarInsn(ISTORE, 4); mv.visitLabel(labelStart); mv.visitVarInsn(ILOAD, 4); mv.visitVarInsn(ILOAD, 2); mv.visitJumpInsn(IF_ICMPGE, labelEnd); mv.visitVarInsn(ILOAD, 3); mv.visitVarInsn(ILOAD, 1); mv.visitInsn(IMUL); mv.visitVarInsn(ISTORE, 3); mv.visitIincInsn(4, 1); mv.visitJumpInsn(GOTO, labelStart); mv.visitLabel(labelEnd); mv.visitVarInsn(ILOAD, 3); mv.visitInsn(IRETURN); mv.visitMaxs(2, 5); mv.visitEnd();  mv = cw.visitMethod(ACC_PUBLIC, "calc", "(II)I", null, null); Label labelStart = new Label(); Label labelEnd = new Label(); mv.visitCode(); mv.visitInsn(ICONST_1); mv.visitVarInsn(ISTORE, 3); // int local3 = 1 mv.visitInsn(ICONST_0); mv.visitVarInsn(ISTORE, 4); // int local4 = 0 mv.visitLabel(labelStart); mv.visitVarInsn(ILOAD, 4); mv.visitVarInsn(ILOAD, 2); mv.visitJumpInsn(IF_ICMPGE, labelEnd); // if (local4 >= local2) goto end mv.visitVarInsn(ILOAD, 3); mv.visitVarInsn(ILOAD, 1); mv.visitInsn(IMUL); mv.visitVarInsn(ISTORE, 3); // local3 = local3 * local1; mv.visitIincInsn(4, 1); // local4++ mv.visitJumpInsn(GOTO, labelStart); // goto start mv.visitLabel(labelEnd); mv.visitVarInsn(ILOAD, 3); mv.visitInsn(IRETURN); // return result mv.visitMaxs(2, 5); mv.visitEnd();  mv = cw.visitMethod(ACC_PUBLIC, "calc", "(II)I", null, null); Label labelStart = new Label(); Label labelEnd = new Label(); mv.visitCode(); mv.visitInsn(ICONST_1); mv.visitVarInsn(ISTORE, 3); mv.visitInsn(ICONST_0); mv.visitVarInsn(ISTORE, 4); mv.visitLabel(labelStart); mv.visitVarInsn(ILOAD, 4); mv.visitVarInsn(ILOAD, 2); mv.visitJumpInsn(IF_ICMPGE, labelEnd); mv.visitVarInsn(ILOAD, 3); mv.visitVarInsn(ILOAD, 1); mv.visitInsn(IMUL); mv.visitVarInsn(ISTORE, 3); mv.visitIincInsn(4, 1); mv.visitJumpInsn(GOTO, labelStart); mv.visitLabel(labelEnd); mv.visitVarInsn(ILOAD, 3); mv.visitInsn(IRETURN); public void calc(int x, int y) { mv.visitMaxs(2, 5); mv.visitEnd(); int result = ; for (int i = 0; i < y; i++) { result = result * x; } return result; } package com.test; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Type; import org.objectweb.asm.commons.GeneratorAdapter; import org.objectweb.asm.commons.Method; import java.io.PrintStream; import static org.objectweb.asm.Opcodes.*; public class Test { public static void main(String[] args) throws Exception { ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); cw.visit(V1_6, ACC_PUBLIC, "test/TestClass", null, "java/lang/Object", new String[] { "java/lang/Runnable" });

// Create Constructor MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "", "()V", null, null); GeneratorAdapter ga = new GeneratorAdapter(mv, ACC_PUBLIC, "", "()V"); ga.loadThis(); ga.invokeConstructor(Type.getType(Object.class), Method.getMethod("void ()")); ga.returnValue(); ga.endMethod();

// public void run(); mv = cw.visitMethod(ACC_PUBLIC, "run", "()V", null, null); ga = new GeneratorAdapter(mv, ACC_PUBLIC, "run", "()V"); ga.getStatic(Type.getType(System.class), "out", Type.getType(PrintStream.class)); ga.push("Hello World"); ga.invokeVirtual(Type.getType(PrintStream.class), Method.getMethod("void println(String)")); ga.returnValue(); ga.endMethod();

cw.visitEnd(); final byte[] classBytes = cw.toByteArray();

new ClassLoader() {{ ((Runnable)defineClass(classBytes, 0, classBytes.length).newInstance()).run(); }}; } }

 Basics for transforming a class ◦ ClassReader cr = new ClassReader(orgBytes); ClassWriter cw = new ClassWriter();

ClassVisitor ca = new MyClassVisitor(cw);

cr.accept(ca);

byte[] transformedBytes = cw.toByteArray();

◦ class MyClassVisitor extends ClassVisitor { public MethodVisitor visitMethod(...) { MethodVisitor mv = super.visitMethod(...); if (!name.equals(“”)) mv = new MyMethodVisitor(mv); return mv; } }  Insert code at the beginning of methods

◦ class MyMethodVisitor extends MethodVisitor { public void visitCode() { super.visitCode(); visitLdcInsn(“Entering method: ” + methodName); visitMethodInsn(INVOKESTATIC, “MyHelper”, “print”, “(Ljava/lang/String;)V”); } }  Modifying opcodes ◦ Changing all hardcoded string to lowercase

◦ class MyMethodVisitor extends MethodVisitor { public void vistLdcInsn(Object cst) { if (cst instanceof String) cst = ((String)cst).toLowerCase(); super.visitLdcInsn(cst); } }  Modifying opcodes ◦ Calling a different method  Example, change from print() to println()

◦ class MyMethodVisitor extends MethodVisitor { public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) { if ((name.equals(“print”) && owner.equals(“java/lang/PrintStream”)) name = “println”; super.visitMethodInsn(opcode, owner, name, desc, itf); } }  Modifying opcodes ◦ Removing method calls  Example, remove all calls to println()

◦ class MyMethodVisitor extends MethodVisitor { public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) { if ((name.equals(“println”) && owner.equals(“java/lang/PrintStream”)) { List argTypes = Arrays.asList(Type.getArgumentTypes(desc)); Collections.reverse(argTypes); for (Type t: argTypes)) { visitInsn(t.getSize() == 2 : POP2 : POP); ◦ } visitInsn(POP); } else super.visitMethodInsn(opcode, owner, name, desc, itf); } }

 Build-time generation

 Build-time post processing

 Runtime generation and scanning

 Load-time instrumentation

 Compilers ◦ You should know what this is already!

 If not, you’ll probably fail this course 

 Retro translation ◦ Transforming bytecode to be compatible with earlier versions  Conversion of added features in bytecode  LDC class constant from Java 5 on  INVOKEDYNAMIC for creating Lambda classes

 Conversion of added API  Integer.valueOf(x) -> new Integer(x) for JDK 5->4  Example ◦ Java 5+ version: LDC com/pkg/Some.class

◦ Java 4 version: GETSTATIC $class$com_pkg_Some IFNONNULL :OK LDC “com/pkg/Some” INVOKESTATIC Class.forName(String):Class PUTSTATIC $class$com_pkg_Some :OK GETSTATIC $class$com_pkg_Some  Shading ◦ The act of renaming classes and usages thereof

◦ Often used to bundle libraries  Shading them with unique package prefix so they don’t interfere with other versions of same library present on class path

◦ Often very fast, as it can be done by changing constant pool entries  Process classes in an archive ◦ Modify the bytecode to map classes to new name

 Example, ASM is bundled in the JDK ◦ Multiple times!

◦ jdk.internal.org.objectweb.asm ◦ com.sun.xml.internal.ws.org.objectweb.asm  Usually done as part of the build tool chain

 Examples ◦ Maven:  maven-shade-plugin  https://maven.apache.org/plugins/maven-shade-plugin/

◦ Gradle:  Gradle Shadow  https://github.com/johnrengelman/shadow  Obfuscation ◦ The act of obfuscating the bytecode, to make it harder to decompile

 Common patterns ◦ Renaming classes and members ◦ Stripping debug information ◦ Mask String-literals by applying a function  Advanced patterns ◦ Utilize invokedynamic and custom callsites, to mask method invocations  Why obfuscate? ◦ is easy to reverse

◦ javac compiler is “stupid”  Doesn’t optimize code  Common patterns for everything

◦ Obfuscation makes it harder to read, not impossible  Renaming classes and members ◦ Similar to shading

◦ Utilizes that return type is part of the method descriptor to rename many methods to same name

◦ Have to remember to exempt public API or implementations of exposed API!  Renaming ◦ Before  class Person {  private String name;  private int age;  public Person() { ... }  public void greet() { ... }  public void setName(String name) { this.name = name; }  public String getName() { return name; }  public void setAge(int age) { this.age = age; }  public int getAge() { return age; }  }  Renaming ◦ After  class a {  private String a;  private int a;  public a() { ... }  public void a() { ... }  public void a(String) { this.a = $1; }  public String a() { return a; }  public void a(int) { this.a = $1; }  public int a() { return a; }  }  String-literal obfuscation ◦ Before  static final String url = “http://google.com”;

◦ After  static final String url = $deopf$(“uggc://tbbtyr.pbz”);  private static String $deopf$(String) { … }

◦ Algorithm used for obfuscating the String varies

 Dynamic Proxies ◦ Dynamically generates bytecode at runtime ◦ Used exhaustively in Java EE  CDI (Contexts and Dependency Injection)  JMS (Java Message Service)  EJB (Enterprise JavaBean)  etc.  Example providers

◦ java.lang.reflect.Proxy (JDK Proxy)

◦ Cglib

 Generate implementations of interfaces dynamically at runtime (or sub types)  Example ◦ Proxy.newProxyInstance(… Runnable.class ...) ◦ Will create a class akin to  class $Proxy1 extends Proxy implements Runnable { public void run() { handle.invoke(...) } /* ... More method ... */ } ◦ Delegating all invocations on the interface to the invocation handler

 Class loader specific instrumentation ◦ Use custom ClassLoader that performs the transformation before defining the class

 Global instrumentation ◦ Use the Instrumentation API  https://docs.oracle.com/javase/8/docs/api/java/lang/ins trument/package-summary.html ◦ You register a transformer, that is invoked for every class loaded afterwards ◦ Often done with Java Agent (-javaagent)  Profilers, APMs, etc ◦ Injects code to monitor usage, performance, resources, etc  JVisualVM, YourKit, JProfiler  XRebel, New Relic, AppDynamics

 Other agent ◦ Load-time-weaving (e.g. AspectJ) ◦ Reload agents (e.g. JRebel)  Aspect Oriented Programming

◦ Load-time weaving of aspects  Adding aspects to methods at runtime  Changing  Removing

 Classic tracing profiling can be considered an around advice ◦ Basic tracing profiler:  Before:  {  methodBody();  }

 After:  {  Profiler.enterMethod(...);  try { methodBody(); }  finally { Profiler.exitMethod(...); }  }  Basics for transforming a class ◦ ClassReader cr = new ClassReader(orgBytes); ClassWriter cw = new ClassWriter();

ClassVisitor ca = new MyClassVisitor(cw);

cr.accept(ca);

byte[] transformedBytes = cw.toByteArray();

◦ class MyClassVisitor extends ClassVisitor { public MethodVisitor visitMethod(...) { MethodVisitor mv = super.visitMethod(...); if (!name.equals(“”)) mv = new TracingMethodVisitor(mv); return mv; } }  Injecting tracing code into method

◦ Class TracingMethodVisitor extends MethodVisitor { private final Label labelStart = new Label(), labelFinally = new Label(); public void visitCode() { super.visitCode(); visitMethodInsn(INVOKESTATIC, “Profiler”, “enterMethod”, “()V”); visitTryCatchBlock(labelStart, labelFinally, labelFinally, null); visitLabel(labelStart); }

public void visitInsn(int opcode) { if (opcode == ATHROW || opcode == RETURN || ...) { visitMethodInsn(INVOKESTATIC, “Profiler”, “exitMethod”, “()V”); } }

public void visitMaxs(int s, int l) { visitLabel(labelFinally); visitInsn(RETURN); super.visitMaxs(s, l); } }  JRebel enabled developers to instantly see code changes in the running application

 Heavily relies on bytecode transformation to  Prepare classes so they can be reloaded  Reload the code when new code is available  public void test() {  assertEquals(++i, method());

 }

 public static int method() {

 return 2;  }  public void test();

 Code:

 0: aload_0

 1: dup

 2: getfield #2 // Field i:I

 5: iconst_1

 6: iadd

 7: dup_x1

 8: putfield #2 // Field i:I

 11: invokestatic #3 // Method method:()I

 14: invokestatic #4 // Method assertEquals:(II)V

 17: return

 public static int method();

 Code:

 0: iconst_2

 1: ireturn  public void test();  71: dup

 0: aload_0  72: invokestatic #93 // Method zt/atf.isNoForwarding:(LObject;)Z

 1: invokevirtual #64 // Method zt/F._jr$iCur:()Z  75: ifne 80

 4: ifeq 25  78: pop

 7: aload_0  79: return

 8: dup  80: pop

 9: invokestatic #68 // Method _jr$ig$i:(LTest$$M$_jr_1;)I  81: goto 7

 12: iconst_1

 13: iadd  public static int method();

 14: dup_x1  0: ldc #30 // class Test$$M$_jr_1

 15: invokestatic #42 // Method _jr$ip$i:(LTest$$M$_jr_1;I)V  2: getfield #38 // Field Class._jr$isRel:Z

 18: invokestatic #69 // Method method:()I  5: ifne 10

 21: invokestatic #70 // Method assertEquals:(II)V  8: iconst_2

 24: return  9: ireturn

 25: ldc #30 // class Test$$M$_jr_1  10: ldc #30 // class Test$$M$_jr_1

 27: getfield #38 // Field Class._jr$isRel:Z  12: getfield #38 // Field Class._jr$isRel:Z

 30: ifne 56  15: ifne 22

 33: aload_0  18: invokestatic #69 // Method method:()I

 34: invokestatic #76 // Method zt/ata.getCurrentVersion:(Lzt/F;)Lzt/F;  21: ireturn

 37: dup  22: ldc #30 // class Test$$M$_jr_1

 38: aload_0  24: invokestatic #51 // Method zt/atr.getMeta:(LClass;)Lzt/C;

 39: if_acmpeq 80  27: iconst_2

 42: dup  28: aconst_null

 43: instanceof #30 // class Test$$M$_jr_1  29: ldc #96 // String method

 46: ifeq 80  31: getstatic #99 // Field java/lang/Integer.TYPE:Ljava/lang/Class;

 49: checkcast #30 // class Test$$M$_jr_1  34: invokestatic #89 // Method zt/atf.invokeWithNoParams:(...)LObject;

 52: invokevirtual #78 // Method test:()V  37: dup

 55: return  38: invokestatic #93 // Method zt/atf.isNoForwarding:(LObject;)Z

 56: ldc #30 // class Test$$M$_jr_1  41: ifne 51

 58: invokestatic #51 // Method zt/atr.getMeta:(LClass;)Ltz/reload/C;  44: checkcast #98 // class java/lang/Integer

 61: iconst_1  47: invokevirtual #102 // Method java/lang/Integer.intValue:()I

 62: aload_0  50: ireturn

 63: ldc #79 // String test  51: pop

 65: getstatic #85 // Field java/lang/Void.TYPE:Ljava/lang/Class;  52: goto 8

 68: invokestatic #89 // Method zt/atf.invokeNoParams:(...)LObject;  public void test();

 0: aload_0

 1: invokevirtual #64 // Method zt/F._jr$iCur:()Z

 4: ifeq 25

 7: aload_0

 8: dup

 9: invokestatic #68 // Method _jr$ig$i:(LTest$$M$_jr_1;)I

 12: iconst_1

 13: iadd

 14: dup_x1

 15: invokestatic #42 // Method _jr$ip$i:(LTest$$M$_jr_1;I)V

 18: invokestatic #69 // Method method:()I

 21: invokestatic #70 // Method assertEquals:(II)V

 24: return

 25: aload_0

 26: invokedynamic #81, 0 // INDY #0:test:(LTest$$M$_jr_1;)V

 31: return

 public static int method();

 0: ldc #30 // class Test$$M$_jr_1

 2: getfield #38 // Field Class._jr$isRel:Z

 5: ifne 10

 8: iconst_2

 9: ireturn

 10: invokedynamic #83, 0 // INDY #1:method:()I

 15: ireturn