Using Dotfuscator Community Edition with Xamarin
We've previously blogged about Using Dotfuscator Professional with Xamarin Applications. We've also blogged about the recently added command line support for Dotfuscator Community Edition. Now it is time to put these two concepts together, and show how to make reverse-engineering your Xamarin application more difficult by integrating Dotfuscator Community Edition (CE) into your Visual Studio build process.
Beginning with Visual Studio 2015 Update 3, the CE command line interface is already installed and available to any registered user, so if you have the latest Visual Studio 2015, no additional downloads are required. If you have a version of Visual Studio 2015 lower than Update 3, you should first go get the newer version of CE that includes the command line interface.
If you are using Visual Studio 2017, see the Dotfuscator CE User Guide for instructions on installing Dotfuscator CE with Visual Studio.
As in our previous Xamarin blog post, we will use MSBuild to integrate Dotfuscator directly into the Xamarin build pipeline, so that your normal workflow will not need to change. There are two main differences in how we will use MSBuild:
- Instead of using the "Dotfuscate" MSBuild task provided with the Professional Edition, we will use MSBuild's "Exec" task to invoke Dotfuscator via its command line interface.
- Instead of using Dotfuscator Professional's post-build events to copy obfuscated output files, we will use MSBuild's "Copy" task.
For our target application, we will use the Android version of BugSweeper, a sample application from Xamarin, available here.
It's always a useful exercise to first see what the unobfuscated application looks like in a decompiler. Try building the "BugSweeper.Android" project in the Release configuration and opening the two managed application DLLs with a .NET decompiler like ILSpy. They are located in the project's bin\Release directory (BugSweeper.dll and BugSweeper.Android.dll). In ILSpy, you will see something like this, which looks a lot like the original source code:
In Xamarin.Android applications, these managed assemblies are included in the output APK, and attackers can easily find and extract them.
Protect the Application with Renaming
Set up Dotfuscator Community Edition
You will need to register your copy of Dotfuscator Community Edition to enable the command line interface. You can do that by launching the Dotfuscator GUI via the "PreEmptive Protection - Dotfuscator" command on Visual Studio's Tools menu (if you do not see this option, see these instructions for Visual Studio 2015 and these instructions for Visual Studio 2017). If this is your first time running it, you will be asked to accept the EULA, then you will be prompted to register. You can also register at any time by selecting the "Register Product" command from the Help menu.
Once your copy is registered, run the command line interface to verify that it is enabled. It is typically located here:
[Program Files]\Microsoft Visual Studio 14.0\PreEmptive Solutions\
Dotfuscator and Analytics Community Edition\dotfuscatorCLI.exe
If everything is working, you should see the command line help on your console with no error or warning messages.
Create the Dotfuscator project file in the CE GUI
Next, create your Dotfuscator configuration file for the project, using the CE GUI.
Define a project property called "OutDir" for the directory containing the input assemblies. To make the Dotfuscator configuration file portable, we will specify the input assemblies relative to this project property. Project properties can be created and edited on Dotfuscator's Properties page, as shown below. Set the "OutDir" property to a directory containing the input assemblies (in this case, it is the Android project's "bin\Debug" directory). At build time, MSBuild will pass a value that overrides this with a configuration dependent value.
Now add the input assemblies. The Android application has two application assemblies: the portable code is in BugSweeper.dll, and the Android specific code is in BugSweeper.Android.dll. Add these two assemblies to Dotfuscator on the Inputs page. Instead of browsing for them on the filesystem, reference them using the "OutDir" project property:
Leave the platform dependent assembly BugSweeper.Android.dll in Library Mode. This tells Dotfuscator not to rename the assembly's visible symbols. This is appropriate, since this assembly contains the entry points to the managed code, called by the runtime. On the other hand, the platform neutral BugSweeper.dll, containing most of the application code, does not need to be in library mode, so uncheck the box:
Some names in BugSweeper.dll need to be excluded from renaming, because they are referenced from the application's XAML in ways that will break if they are renamed. In Dotfuscator, you can exclude specific symbols from renaming from the Renaming -> Exclusions page. Exclude these symbols:
When you are finished configuring, save the Dotfuscator project to the BugSweeper.Android project's directory. Name the file "Obfuscate.Android.CE.xml".
Modify the Xamarin project file
The next step is to get Dotfuscator running as part of the Bugsweeper.Android project's build.
Open the BugSweeper.Android.csproj file in a text editor, and add the following XML to the "AfterBuild" target, which will invoke Dotfuscator CE via the Exec task. Then it will copy the output assemblies back to their original locations, so that the remaining steps of the Xamarin build will operate on them:
<Target Name="AfterBuild"> <Exec Command=""C:\Program Files (x86)\Microsoft Visual Studio 14.0\PreEmptive Solutions\Dotfuscator and Analytics Community Edition\DotfuscatorCLI.exe" -p=OutDir=$(OutDir) $(ProjectDir)\Obfuscate.Android.CE.xml" /> <ItemGroup> <ObfuscatedFiles Include="$(ProjectDir)\Dotfuscated\*.dll" /> </ItemGroup> <Copy SourceFiles="@(ObfuscatedFiles)" DestinationFolder="$(OutDir)" /> <Copy SourceFiles="$(ProjectDir)\Dotfuscated\BugSweeper.dll" DestinationFolder="$(ProjectDir)\..\BugSweeper\$(OutputPath)" /> </Target>
Note that this uses Dotfuscator's /p option to pass the value of MSBuild's $(OutDir) into Dotfuscator's corresponding "OutDir" project property. This overrides the hardcoded definition in the Dotfuscator configuration file, and therefore ensures that Dotfuscator will always process the correct assemblies for the current build configuration.
Build the Project
Set Visual Studio's "MSBuild project build output verbosity" option to "Detailed" if you want to see Dotfuscator's output in the VS Output Window during the build. You can find this setting in Visual Studio's Options dialog as shown:
Building the project results in output like this when Dotfuscator runs:
Verify the Renaming
To see how the renaming worked, open the output assemblies in a decompiler as before. Here is a view of the same decompiled code as shown above, after being built with Dotfuscator:
The renaming has provided a good layer of obfuscation that hides much of the code's intent and makes it more difficult to follow.
Dotfuscator Professional Edition includes additional transforms, such as control flow obfuscation and string encryption, that provide even more protection by making the decompiler output incorrect code, or no code at all:
Try them out for yourself with a Dotfuscator Professional Edition evaluation!