//
you're reading...
Java BCI

How To: BCI a class during runtime

BCI (byte code injection) can be done either statically or dynamically. Static BCI is when the byte code is injected and stored into a class file. The modified class is loaded instead of the original class, by modifying the classpath from where the class is picked up. Dynamic BCI, does not do any permanent changes to the class file, but varies the class bytes when it is loaded. This article talks about dynamic BCI using ASM library by using a non-delegating classloader. Here we will add a new field to the a class dynamically.

The high level steps involved in BCI’ing a class are:

  • Write a class that modifies the bytes
  • Create a custom classloader that overrides findClass

Write a class the modifies the bytes

The ASM library is used to modify the class bytes. ASM uses the visitor pattern to modify bytes. A tutorial can be found here. Multiple visitors exist which can be used to modify the bytes. Here we will use the ClassVisitor and override the visitEnd method to add the new field. The visitEnd method is called after all the methods and fields of the loaded class is traversed.

We will write a FieldAdder class the extends ClassVisitor:

    public class FieldAdder extends ClassVisitor
    {
        FieldNode _nde;
        public FieldAdder(ClassVisitor cv)
        {
            super(1, cv);
            _nde = new FieldNode(Opcodes.ACC_PRIVATE, "_trial", "Ljava/lang/String;", null, null);
        }

        public void visitEnd()
        {
            System.out.println("In visit End. Adding Fields");
            _nde.accept(cv);
            super.visitEnd();
        }
    }

The important code is marked in bold. A FieldNode is a representation of a field in the class. A new FieldNode is created with the appropriate access (OpCodes.ACC_PRIVATE), name (_trial) and type (Ljava/lang/String). In the visitEnd, this is accepted into the ClassVisitor (cv is the ClassVisitor variable of the class being traversed).

To use this ClassVisitor to modify bytes we need to call the following lines of code:

        
    public byte[] modifyClass(String cls)
        throws Exception
    {
        InputStream str = new FileInputStream("loadcls/" +cls + ".class");
        ClassReader reader = new ClassReader(str);
        ClassWriter cw = new ClassWriter(reader, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
        FieldAdder fadder = new FieldAdder(cw);
        reader.accept(fadder, 0);
        return cw.toByteArray();
    }

The ClassReader reads the class of the InputStream passed. The FieldAdder that we defined previously is used as a visitor when the class is being read. The modified bytes can be accessed via the ClassWriter used to create the FieldAdder. The references for ClassReader and ClassWriter can be found here. Now all that is left is to call this at the correct location. For this we will write a ClassLoader that calls this.

Create a Custom ClassLoader

We will create a delegating ClassLoader. This implies that if the class is present in the parent classpath, then the class is not BCI’ed, if not present it is loaded from a relative path of “loadcls/*” and BCI’ed.

    public static class MyClassLoader extends ClassLoader 
    {
        protected Class findClass(String name) 
            throws ClassNotFoundException
        {
            try
            {
                byte[] b = loadClassData(name);
                return defineClass(name, b, 0, b.length);
            }
            catch (Exception e)
            {
                throw new ClassNotFoundException();
            }
        }        private byte[] loadClassData(String name) 
            throws Exception
        {
            TestAddField tst = new TestAddField();
            return tst.modifyClass(name);
        }
    }

In the above, we have overridden the findClass method to call our modifyClass function. The modified bytes are then used to define the class.

To BCI any class we load the class using the above classloader.

MyClassLoader ldr = new MyClassLoader();
Class cls = ldr.loadClass("ClassToAddField");

Introspecting the fields in the class loaded will show that it has the new field added to it. The Classes explained here is present here. Download the asm 4.0_RC2 jar files and run the commands in compileaddfield.sh to see it in action.

 

Discussion

Trackbacks/Pingbacks

  1. Pingback: HOW TO: Stereotyping a java Class | Reflections - July 20, 2013

  2. Pingback: HOW TO: BCI method calls into java methods | Reflections - July 23, 2013

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 145 other followers

%d bloggers like this: