Renaming
Overview
Dotfuscator is capable of renaming all classes, methods, and fields to short names. This makes decompiled output much more difficult to understand and makes the resulting executable smaller in size.
Most commercial obfuscators employ a renaming technique that applies trivial identifiers that can be as short as a single character. As the obfuscator processes the code, it selects the next available trivial identifier for substitution. This seemingly simple renaming scheme has a key attribute: it cannot be reversed. While the program logic is preserved, the names become nonsense, hampering all attempts to understanding the code.
Overload Induction
Dotfuscator uses a deeper form of obfuscation, developed for Dotfuscator and patented by PreEmptive Solutions, called Overload Induction™. Instead of substituting one new name for each old name, Overload Induction renames as many methods as possible to the same name. After this deep obfuscation, the logic, while not destroyed, is beyond comprehension. The following simple example illustrates the power of the Overload Induction technique:
Original Source Code Before Obfuscation:
private void CalcPayroll(SpecialList employeeGroup) {
while (employeeGroup.HasMore()) {
employee = employeeGroup.GetNext(true);
employee.UpdateSalary();
DistributeCheck(employee);
}
}
Reverse-Engineered Source Code After Overload Induction Obfuscation:
private void a(a b) {
while (b.a()) {
a = b.a(true);
a.a();
a(a);
}
}
The example shows that the code is obfuscated and compacted, a positive side effect of renaming.
For example, if a name is 20 characters long, renaming it to a()
reduces its size by 95%.
Renaming also saves space by conserving string heap entries.
Renaming everything to "a
" means that a
is stored only once, and each method or field renamed to a
can point to it.
Overload Induction enhances this effect because the shortest identifiers are continually reused.
Many customers report a full third of all methods being renamed to "a()
".
There are several distinct advantages to this methodology:
- Renaming makes decompiled output difficult to understand.
Renaming to unprintable characters or illegal names in the target source language is futile since decompilers are equipped to re-rename such identifiers.
Considering that Overload-Induction could make one of three method names "
a()
", understanding decompiled output is difficult. - Overload-Induction has no limitations except those that exist in all renaming systems.
- Since overload-induction tends to use the same letter more often, it reaches into longer-length names more slowly (e.g.
aa
,aaa
, etc.). This also saves space.
Overload-Induction’s patented algorithm determines all possible renaming collisions and only induces method overloading when it is safe to do so. The procedure is provably irreversible. In other words, it is impossible (even via running Overload-Induction again) to reconstruct the original method name relationships.
Enhanced Overload Induction
Dotfuscator also provides Enhanced Overload-Induction™ by allowing a method’s return type or a field's type to be used as a criterion in determining method or field uniqueness. This feature can allow up to 15% more redundancy in method and field renames. In addition, since overloading on method return type or field type is typically not allowed in source languages (including C# and Visual Basic), this further hinders decompilers.
Limitations
Overload Induction is typically not performed on assemblies containing any XAML code because of the way the runtime determines how XAML and code are linked up. As a result, the Use Enhanced Overload Induction option will not change anything for such assemblies.
Problems can occur with Renaming when using any type of reflection (including XAML), configuration files that specify entry points, libraries that are called by other applications, etc. It is important to fully test applications after Renaming to ensure no such problems exist.
Specific Exclusions
Presuming Library Mode is turned off, Dotfuscator will try to rename everything it possibly can. Certain uses of reflection can cause Dotfuscator to not update all references to a particular entity upon renaming. In the following example, we’ve excluded four classes and their fields:
Our application references these types and members by looking up their names via reflection. If they were to be renamed, the application would be unable to find them at runtime. Therefore, we exclude them from renaming via these checkboxes.
Custom Exclusion Rules
There may be times when you may need to make many specific exclusions for a particular coding convention. And even then, when a developer adds more code that follows that convention, they would have to remember to make yet another specific exclusion for their new code.
The solution to this is Custom Exclusion Rules. Using the previous example, let’s say we figured out that all types with names ending in "Dungeon", and specific types of fields within those types, will need to be excluded from renaming for some reason. Rather than creating a bunch of specific exclusions, we can make one custom rule:
The rule is displayed on the right pane.
We added the rule's root node with the Add Type button and given it the name .*Dungeon
and set the Regular Expression (regex
) option to true
to indicate that the name should be treated as a regular expression.
Thus, the node will match types whose names end in "Dungeon".
Because we have set Exclude Type (excludetype
) to true
, any types matching this name will be excluded from renaming.
If we had set that option to false
, the types that match would not be excluded from renaming - only the members specified by their child nodes.
We then added a child node by right-clicking the root node and selecting, in this case, Add Field.
We gave this node the name .*
and again indicated that this should be treated as a regular expression.
We've restricted the rule to only cover fields which have the public access modifier (by setting +public
), and whose signature is string[]
, as both are necessary in our scenario.
Once a rule is configured, you can select a node and click the Preview button, and Dotfuscator will highlight the entries that match your custom rule in the tree view on the left. In our example, we are previewing the child node, so fields that match the criteria are highlighted. Notice that the "Keys" field in "AdventureGame.Validation" is not excluded, because while it matches this child node, its parent type does not match the parent node (i.e., the class name "Validation" does not end in "Dungeon").
The resulting configuration of this custom rule is stored as XML in the config file. This example produces the following config XML:
<type name=".*Dungeon" regex="true">
<field name=".*" speclist="+public" signature="string[]" regex="true" />
</type>
Built-In Rules
Built-In Rules are Custom Exclusion Rules that are fairly universally useful; we include them by default so users do not have to re-implement them.
The exact definitions of each of these rules can be found in your installation location's Common
subdirectory.
A typical example is C:\Program Files (x86)\PreEmptive Protection Dotfuscator Professional A.B.C\Common
.
The rule definitions are found in the XML file with a name beginning with dotfuscatorReferenceRule
.
For example, Methods with System.Web.Services attributes translates to the following custom rule:
<excludelist>
<type name=".*" regex="true" excludetype="false">
<comment>Exclude all methods that are decorated with attributes from the System.Web.services namespace.</comment>
<method name=".*" regex="true">
<customattribute name="System.Web.Services.*" regex="true" />
</method>
</type>
</excludelist>
Options
Many renaming options, including the renaming scheme, are documented in the Renaming Editor section.