Dynamic Class Loading
Overview
The forName()
method of java.lang.Class
is the way to load classes dynamically at runtime.
It is impossible for PreEmptive Protection™ DashO™ for Android & Java to determine what classes are dynamically loaded in all cases.
Consider the following code:
public Object getNewClass() {
String newClassName = getUserInputString();
try {
Object newClass = Class.forName(newClassName).newInstance();
return newClass;
} catch(Exception e) {
// handle
}
}
This code loads a class by name and dynamically instantiates it. In addition, the name comes from a string input by the user. There is no way for DashO to predict which class names the user will enter. The solution is to exclude the names of all potentially loadable classes (method and field renaming can still be performed). This is where manual configuration is required.
Note:
Incorrect specification of dynamically loaded classes can cause obfuscated applications to fail at runtime.
Predictable Dynamic-Loading
The simplest case is when you know your application well enough to know exactly what classes could be loaded via dynamic-loading. If the dynamically loaded classes share a base class or common interface:
String s = getShapeName();
Shape myShape = (Shape)Class.forName(s).newInstance();
myShape.draw();
In this example, DashO's can detect this pattern automatically and include all Shape
classes.
If another type of creation pattern is used the classes would need to be added individually in the entrypoints section of the DashO configuration file:
<entrypoints>
<classes name="Triangle"/>
<classes name="Rectangle"/>
</entrypoints>
In this case DashO will be able to remove unused methods from the Shape
hierarchy.
Unpredictable Dynamic-Loading
In cases where the dynamically loaded classes would not know at the time the application is obfuscated, for example, a user interface building application could allow users of the application to include their own or third-party components, the existing classes must be added as unconditional entry points.
<entrypoints>
<unconditional name="Triangle"/>
<unconditional name="Rectangle"/>
</entrypoints>
This has several ramifications:
Regardless of removal options, no methods or fields will be removed from an unconditionally included class.
Regardless of renaming options, neither the class nor its members will be renamed.
All methods within the class will be treated as entry point methods.
These rules enforce the idea that your interface to as-yet-unknown classes will remain intact.
Reflection Report
DashO has several facilities to allow you to specify how or what is dynamically loaded. The fornamedetection option in DashO handles most or all dynamically loaded class instances.
<global>
<option>fornamedetection</option>
</global>
DashO reports all places it finds usage of forName()
.
This is provided as part of the report file and as output after dependency analysis.
Note that the fornamedetection option will not give a wrong answer but it may give no answer at all.
Manual configuration is required in those instances where DashO reports “unable to determine” dynamically loaded class.
NOTE: Reflection use public void com.yoyodyne.Application.getInterface() – java.lang.Class.newInstance() - [BaseInterface Possible: InterfaceImplementor] Reflection use public boolean com.yoyodyne.Test.connect() - Class.forName() Reflection use public float com.yoyodyne.Test.calculate() - Class.forName() - [com.yoyodyne.Linker]
Since DashO is unable to determine what class is dynamically loaded in the method connect()
, manual configuration becomes necessary.
The class that is dynamically loaded in this method must be included using the <classes>
element under the <entrypoints> section.
If DashO finds reflection usage and you do not specify the force global option, DashO will not create any output classes or jars.