Tuesday, September 04, 2007

Fishy Serialization

In Java, "Object Serialization supports the encoding of objects and the objects reachable from them, into a stream of bytes". This representation of the object can then be used for purposes like persisting on disk or passing of objects over the network. To serialize an object the class needs to implement the Serializable interface which is a marker interface with no methods defined. Optionally the class may define "readObject" and "writeObject" methods which will be used in the serialization process as defined in Specification.

The signature of the read/write Object methods is what struck me recently(this implies that I've been reading serialization related code before and not realised this). The modifier is private which means that no other instance should be able to invoke that method! And yet its invoked somehow. Time for some hacking...


For a dummy class Dog the stack trace to call the writeObject from SerializeTest.main was:

java.lang.RuntimeException
at foo.bar.Dog.writeObject(SerializeTest.java:81)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:945)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1461)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1392)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1150)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:326)
at foo.bar.SerializeTest.main(SerializeTest.java:23)

The ObjectOutputStream.writeObject is invoked from the main method and after lots of calls ObjectStreamClass.invokeWriteObject is called which does some reflection The writeObject method is dynamically invoked there. The interesting part of the implementation is:

void invokeWriteObject(Object obj, ObjectOutputStream out) {
writeObjectMethod.invoke(obj, new Object[]{ out });
}


writeObjectMethod is a member variable in ObjectStreamClass of type java.lang.reflect.Method
and is set as:

writeObjectMethod = getPrivateMethod(cl, "writeObject",
new Class[] { ObjectOutputStream.class },
Void.TYPE);


The definition of ObjectStreamClass.getPrivateMethod is:

/**
* Returns non-static private method with given signature defined by given
* class, or null if none found. Access checks are disabled on the
* returned method (if any).
*/
private static Method getPrivateMethod(Class cl, String name, Class[] argTypes,
Class returnType) {
Method meth = cl.getDeclaredMethod(name, argTypes);
meth.setAccessible(true);
int mods = meth.getModifiers();
return ((meth.getReturnType() == returnType) && ((mods & Modifier.STATIC) == 0) &&
((mods & Modifier.PRIVATE) != 0)) ? meth : null;
}
}


And there is the call to the method which does all that magic - Method.setAccessible.

The javadoc for the method says:
"A value of true indicates that the reflected object should suppress Java language access checking when it is used"

Using Reflection and with proper access its possible to even call private methods. This was something cool that I've learnt in a long time. Certainly makes Java more dynamic in nature. Now I'll have to read more on the Java security API soon.