Ashok Purushotham
(ashok@cs.arizona.edu)
Rathna Prabhu Rajendran
(prabhu@cs.arizona.edu)
A class C is split into two classes C1 and C2, such that C2 inherits from C1. C1 has fields and methods that only refer to themselves, whereas C2 has fields and methods that can refer to themselves as well as fields and methods in C1. Bytecode references to C will have to be replaced with references to C2.
Dependency Graph Generation
We create a dependency graph G for class C. The nodes of G are the
members of C. There is an edge from A to B in G, if the declaration of A
must be in scope of B. If there is a path from C to Y in G, then Y must
be declared in the child class during splitting. If there is a path from
X to Y in G, then either X and Y are both declared in the same class or
X is declared in the parent class C1.
Topological sort of the Dependency Graph
We perform a topological sort of the dependency graph to know what
methods and variables are to be put in which classes. We decide on the
placement of the methods and fields in the inheritance tree, based on
the results of the topological sort. For instance, the method P must be
in the scope of the class in which Q is defined. So Q can be put in the
same class as P or in some class inheriting from the class defining P.
This ensures that the obfuscation is semantics preserving and gives the
same results as before.
Consider the following simple code sample C.java, to which we'll apply the obfuscation technique.
class C { float f; int v; public C( ) { } public void P( ) { f = 1.2; } public void Q( ) { v = 1; P( ); } }
After obfuscating:
class C0 { float f; int v; public C0( ) { } } class C1 extends C0 { public C1( ) { } public void P( ) { f = 1.2; } } class C extends C1 { public C( ) { } public void Q( ) { v = 1; P( ); } }
No additional configuration parameters are required to carry out this obfuscation.