With prior versions of PostSharp, you had to be very careful to make your software compatible with any obfuscator. You could not obfuscate aspect classes, you could not store reflection objects, and you could not add use a reflection object in RuntimeInitialize with obfuscated generic methods. Indeed, all these features caused PostSharp to store the name of the metadata objects at build time, and to deserialize it at run time. But since the obfuscator renamed metadata objects after build time, the deserialization failed. Briefly said, previous versions of PostSharp were incompatible with all obfuscators.
PostSharp 2,1 provides a mechanism through which assemblies can be “repaired” after they have been obfuscated. Assemblies are still fully obfuscated, but PostSharp provides a utility to repair the name-object table. The build process is the following:
1. The compiler produces an assembly.
2. PostSharp post-processes the assembly and embeds an name table in the output assembly.
3. The obfuscator renames metadata objects, therefore breaks the PostSharp name table.
4. The post-obfuscation step (implemented by PostSharp) repairs the name table based on the obfuscation map produced by the obfuscator, and mapping the old name to the new name.
Since the post-obfuscation utility works with the obfuscation map, we need one implementation of this utility for every obfuscation map format, therefore for every obfuscation tool. PostSharp 2.1 includes support for Dotfuscator, the leading obfuscator .NET.
Calling the post-obfuscator
Let’s see how it works practically. You can download the example and try yourself.
To call the post-obfuscation step, you have to create an MSBuild project file and import the targets AddIns\PostSharp.AddIn.PostObfuscation.targets.
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Build">
<!-- Required: Path of dependencies of obfuscated assemblies. -->
<ObfuscatedSearchPath Include="Dotfuscated" />
<!-- Required: Path of dependencies of unobfuscated assemblies. -->
<!-- Required: Obfuscation map file produced by Dotfuscator. -->
<!-- Required: Obfuscated files -->
<!-- Optional -->
<!-- Required: output directory -->
<!-- Required: selection of the remapping strategy -->
<Target Name="Build" DependsOnTargets="PostObfuscationRemap">
<!-- Optional: copy other files to the output directory-->
<Copy SourceFiles="@(ContentFiles)" DestinationFolder="$(OutputPath)"/>
<Target Name="Rebuild" DependsOnTargets="Clean;Build">
<Import Project="..\..\Build\bin\AddIns\PostSharp.AddIn.PostObfuscation.targets" />
As you can see, this file defines some locations (required items and properties), and calls the PostObfuscationRemap target defined in PostSharp.AddIn.PostObfuscation.targets.
Safe code references
The post-obfuscator ensures the following references to code are repaired after obfuscation:
- References you are not aware of (used by PostSharp to provide the runtime features).
- Reflection objects (for instance MethodInfo, Type) serialized inside objects using the default serializer (BinaryFormatter). This feature is implemented thanks to serialization surrogates for reflection objects.
There is no magic. Other strings referencing to a metadata element, and stored somewhere else than in a reflection object serialized by the default serializer, will not be fixed. If you wrote your custom aspect serializer and want to take advantage of metadata reference serialization, you have to play with IMetadataEmitter/IMetadataDispenser.
In other words, if you want to write obfuscation-safe aspects, the only serialized metadata references should be reflection objects in an aspect field (or somewhere under an aspect field).
Stripping of events and properties
Dotfuscator allows users to remove events and properties from code. The reason is simple: they are useless at runtime for the CLR. They are pure compiler syntactic sugar… or rather were, before XAML. So what if your aspect stores a PropertyInfo or EventInfo?
PostSharp will be smart enough to provide you with a PropertyInfo or EventInfo. The only visible difference is that the Name property will return null. Other properties and methods will work as usually. Under the hood, PostSharp will give you an object of type ObfuscatedPropertyInfo or ObfuscatedEventInfo instead of RuntimePropertyInfo or RuntimeEventInfo, but your code should not see the difference.
Support for other obfuscators
As a first step, we provide support for Dotfuscator. If other vendors are interested to support PostSharp, we’ll gladly provide them with the specification of the binary format. If you want to contribute a post-obfuscation filter, then be pleased that the Dotfuscator-specific code is only 56 lines long (parsing of their XML obfuscation map). The post-obfuscation add-in is not obfuscated so feel free to use a decompiler :).
That’s it for obfuscation. A small, unexciting feature, but people who need to obfuscate their applications don’t have any excuse not to use PostSharp!