A Much Better Way to Obfuscate Windows Store / Appx Apps
Dotfuscator has supported Windows Store apps (i.e. Appx packages) since 2012, but integrating Dotfuscator into the development workflow has been difficult because of limitations in Visual Studio and the APPX build/publish process.
Until today. We've been working hard to find a way to improve this process, and we've found a way to make it totally automatic, after just a little bit of initial setup. As a warning, this uses an "internal" MSBuild target that may change in later versions of Visual Studio. If it does change, it's likely that this method could be adapted to work, and we'll update this post with new instructions.
This solution provides a number of benefits:
- Automated obfuscation of Appx packages and AppxBundles from within Visual Studio and/or an automated build server (using MSBuild)
- Automated obfuscation of multi-platform Appx and AppxBundle builds
- Both of the above also work for Dotfuscator for Marketplace Apps
After the initial setup, obfuscation will be an automatic part of the "Create App Packages" process in Visual Studio. You'll also be able to use a special build target to launch the Dotfuscator GUI to make it easy to configure obfuscation.
This solution is based on MSBuild, which is what Visual Studio uses under the covers to build your application. Knowledge of MSBuild is not required to follow this tutorial, but it might help make things easier to follow.
This tutorial should work with these versions of Dotfuscator:
- Windows 8 Appx packages: Dotfuscator Professional/Evaluation/Market Place Apps v4.9.8500 (October 2012) and newer
- Windows 8.1 Appx packages or AppxBundles: Dotfuscator Professional/Evaluation/Market Place Apps v4.11 and newer
- Windows 8.1/Windows Phone 8.1 Universal Appx Packages (see other notes below): Dotfuscator Professional/Evaluation v4.11 and newer
This will not work with Dotfuscator Community Edition because it doesn't support MSBuild or obfuscation of marketplace applications.
Note: this is ONLY for Appx packages, not for Silverlight packages or any other package format, although the concepts could be adapted to work with other platforms.
To get to the point where everything just works, some initial setup is required. This involves manually editing some XML files, but should be fairly easy. Here is an overview of what must be done:
- Configure a new ManualObfuscation target in your Visual Studio solution and project
- Download and configure your Visual Studio project to use a custom
- Create or modify a Dotfuscator project file to receive a specific set of Project Properties from the new target(s)
So, let's dive in.
Configure manual obfuscation
First, we must create the Manual Obfuscation configuration for our project. From within Visual Studio:
- Go to Build menu
- Go to Configuration Manager
- Click on the Configuration drop down next to your Windows Store App project
- Click "New..." on the drop down menu
- Name the new configuration "ManualObfuscation"
- Click the drop down to copy settings from Release (or Debug, your choice)
- Ensure that the "Create new solution configurations" checkbox is checked
- Click OK
You should see something like this at step #7:
Download the ObfuscateAppx.targets file
Next, download the ObfuscateAppx.targets file. Place
ObfuscateAppx.targets in the same directory as the .csproj or .vbproj for your Windows Store app. That location is important; you'll need to place a copy of that file in each Windows Store Application project for which you want to do this. This is not required for Windows Store Libraries dependencies to an application, however.
Next, you'll want to at least take a peek at this file. The only thing that might have to be changed is the
<DotfuscatorLocation> property. It's hard-coded to the usual path for Dotfuscator under 64-bit systems. This must be changed if it is used on a 32-bit system and/or with a different version of Dotfuscator.
DotfuscatorLocation can use an environment variable instead of a hard-coded path. This is preferable when using source control so that different environments can be used without changing this file.
For this to use an environment variable, the line with the
<DotfuscatorLocation> tag must be changed to look like so:
This change will make it reference the environment variable DOTFUSCATOR_LOCATION. Make sure to properly set the environment variable before continuing with this tutorial.
Configure the Visual Studio Project
Now we need to tell Visual Studio to use the new obfuscation step that is defined in the
ObfuscateAppx.targets file. To do this, we'll edit your project's
Note that a C# (or VB) project file (
.vbproj) is just a special MSBuild file, so it's easy to just add an extra step using the normal conventions of MSBuild.
First, right click on the project in Solution Explorer. Click on the "Unload Project" option. Now, right click on the project again and click "Edit ... ". Now, scroll all the way to the bottom of the XML. Copy and paste this line:
<Import Project="ObfuscateAppx.targets" />
Put it just before the
</Project> closing tag. Now click save, and right click on the project and click "Reload project".
The Dotfuscator project file
Now a project file for Dotfuscator is needed. There are two options here. The easy option is to download an empty Dotfuscator project that's already setup. But if you already have a Dotfuscator project started, you'll need to modify it to use the exposed project properties.
There are 6 project properties sent to Dotfuscator you can use:
inputdir-- The directory where the appx package is
inputfile-- The appx file(without directory name) you'll be obfuscating
outputdir-- The directory to output the obfuscated appx package to (should be the same as inputdir)
pfxfile-- The private key file used for signing the appx package
mapoutputdir-- The directory the mapfile is output to
mapfile-- The name of the mapfile output
Using the template project
If you haven't already created a Dotfuscator project for this app, you can just download this
dotfconfig.xml to the same directory as
ObfuscateAppx.targets. See the "Ensure configuration filename is correct" section for how to use a different filename than
Initialize the Configuration
Then the blank template configuration file must be "initialized". When you first add an input to a project, Dotfuscator automatically pulls certain metadata out of that input and puts it into the config file, and we need to make sure that metadata is created before you try to obfuscate. To do that, we'll open the template project in the Dotfuscator GUI, modify it slightly, and save it again. That will trigger the initialization that we need.
To do this, change the solution configuration to ManualObfuscation. Then, go to publish an appx package using the "Create App Packages" wizard of Visual Studio.
At this point you should see the standalone Dotfuscator GUI:
Here you can make exclusions or other configuration options. If you don't have any settings to change, you'll still need to make at least one change so that Dotfuscator thinks the project needs to be saved/updated. A quick way is to just toggle "library mode" on the executable twice. After making your changes, save the project.
After you're done, exit Dotfuscator. Then you should see the option to certify the package. You can cancel this for right now
Modifying an existing project
If a Dotfuscator project already exists for this application, it's fairly easy to convert it to use the required project properties. It's easiest to hand-edit the XML to get this to work.
First, I'm assuming that the project is obfuscating the Appx package directly (rather than the executable). Second, I'm assuming that the project only has one input. If multiple packages with different configurations exist, like for x86 and ARM, you should delete the inputs until you're left with just one package.
Next, grab an XML editor, like Visual Studio, and open the Dotfuscator project file.
First, you need to add the list of properties to the Dotfuscator configuration:
<propertylist> <property name="inputdir" value="" /> <property name="outputdir" value="" /> <property name="inputfile" value="" /> <property name="pfxfile" value="" /> <property name="mapoutputdir" value="" /> <property name="mapfile" value="Map.xml" /> </propertylist>
This should go right after the Dotfuscator tag like so:
Afterwards, this is the next relevant bit of the project file:
These changes are needed:
- change the
CertificateFiledirective to use the
- change the file dir to use the
- change the file name to use the
Here's what it looks like after these changes:
Notice that the
PreEmptiveStopWatch.exe attribute doesn't need to be updated. This is because it's within a package tag, which means that it's an assembly inside the package
Now the next bit of XML:
Here, change the output directory to use the
And finally, the renaming report/map file needs to be updated. It should look like so:
Change the two relevant pieces of text so that
- The directory for the mapfile to be output becomes
- The file name for the mapfile to be output becomes
It should look like this afterwards:
That was easy. Notice also that there are a few renaming exclusions for this project in the middle of all of that. These don't need to be touched at all, they will automatically work.
Ensure configuration filename is correct
ObfuscateAppx.targets file assumes that the Dotfuscator configuration file is named
dotfconfig.xml and is located in the same directory as
ObfuscateAppx.targets. So, be sure to rename the configuration file to
ObfuscateAppx.targets can be modified so that it expects a different configuration filename. This can be done by changing the contents of the
<DotfuscatorConfig> tag in the file.
Building Your Package
This is the best part. Make sure the solution configuration is set to Debug or Release (or anything other than "ManualObfuscation"). Make a package using the "Create App Packages" wizard of Visual Studio. If you chose to build a Release configuration, then you should now get an option to certify your package.
Wait, Dotfuscator didn't run! Actually, if all went well, it did. It runs in a completely automated fashion. You can confirm that it obfuscated the package by going into the
AppPackages folder of your project. Then, select the folder for the package that was just created (the highest version number). In this folder, there should be an unobfuscated directory AND a Map.xml file. If those two files exist, it's a fairly good indication that Dotfuscator has obfuscated your package.
You can also look at the build output in Visual Studio, where you should see the log messages from Dotfuscator.
This is what the directory of an obfuscated package should look like:
To modify your Dotfuscator configuration, just change your solution configuration to "ManualObfuscation" and build. That should bring up the Dotfuscator GUI, where you can make all your changes and save. Building from within that GUI will also work. Note, after pushing the Dotfuscator "play" button, you will get a message like
Package C:mypackage.appx of type Appx has changed. Do you want to reload the package?
At this point you should click "No" and exit Dotfuscator afterwards. Obfuscation happens in-place. This means that the source package is eventually replaced with the obfuscated package. If you push the "play" button more than once, you will be reobfuscating a previously obfuscated package. There is not a good way of avoiding this at the moment.
Note that if you add a new assembly to the application, you'll need to do the procedure described above to "Initialize the Configuration". Otherwise, the new assembly will be treated as an artifact and will not be obfuscated.
There is only a single Dotfuscator project file. So, if you're targeting multiple platforms, take note that if you run Manual Obfuscation on more than 1 platform, then you will be modifying the same Dotfuscator project. It is not currently possible to have separate Dotfuscator project files for each platform.
Also, when configuring your Dotfuscator project using Manual Obfuscation, you should only build the application package for one platform. You can do this by ensuring only 1 architecture has a checkbox beside it in the
Select and Configure Packages screen. If you don't do this, you may get an error from Dotfuscator or the Dotfuscator user interface may pop up more than once.
A sample project is also available with all of this already put together. This can be downloaded here: ObfuscateAppxSample.zip
Note: It assumes a hardcoded path to Program Files (x86) for Dotfuscator 4.12. You may have to change this in
ObfuscateAppx.targets for it to work on your system.
From the command line/build server?
This also works from the command line and is suitable for use in a build-server environment. Just use
msbuild MyProject.sln from the command line. Dotfuscator will automatically run when building the package just like it does in Visual Studio.
Does this support multiple platforms/automatic version numbers?
Yes! This will automatically run Dotfuscator multiple times for all of the platforms that Appx packages are created for. It also automatically picks up on the latest version number being used.
How do I obfuscate Universal Apps?
Universal Apps for Windows Store 8.1/Windows Phone 8.1 have a similar workflow to normal Windows Store apps. For the purposes of obfuscation though, the two targets are treated as separate projects. So, this means that you will need separate Dotfuscator configuration files for the Windows Store and Windows Phone projects, and will basically need to repeat the process laid out in this blog post to enable obfuscation support in both the Windows Store and Windows Phone csproj file.
Imported project was not found
If you get this error:
The imported project "C:FooBarObfuscateAppx.targets" was not found. Confirm that the path in the <Import> declaration is correct, and that the file exists on disk
This means you probably copied the ObfuscateAppx.targets file to the wrong place. Ensure that it is in the same folder as your Windows Store App's .csproj or .vbproj file.
The command exited with code 3
If you get an error like this:
The command ""C:Program Files(x86)PreEmptive SolutionsDotfuscator Professional Edition 4.10dotfuscator.exe" "C: ..... /g" exited with code 3
This error means that dotfuscator.exe couldn't be found. If using a hardcoded path for
DotfuscatorLocation, ensure that it is correct. If using an environment variable, ensure that it is properly set. The path to which Dotfuscator is installed can vary depending on your operating system and version of Dotfuscator
The "PreEmptive.Tasks.Dotfuscate" task could not be loaded
If you experience an error like this, it means that MSBuild couldn't find the Dotfuscator MSBuild DLL. Ensure that you are using a supported version of Dotfuscator (CE will not work!). Normally, this file is located in
C:Program FilesMSBuildPreEmptiveDotfuscator4.0. If this file is missing, you probably don't have Dotfuscator installed on your system. The path to which this file is installed can vary depending on your operating system and version of Dotfuscator. If Dotfuscator is properly installed, this shouldn't happen.
There are no assemblies to process
Ensure that your Dotfuscator configuration file is correct. This can be caused by the package
<file> tag not using the correct project properties.
Xml Validation Error...
Ensure that your Dotfuscator configuration file is a valid XML file.
Dotfuscator says Warning Input Assembly appears to be obfuscated
You may see this warning if you do not have Visual Studio set to automatically increment the version number of your package AND you have not modified the source code since last building the package. It's recommended to let Visual Studio automatically increment the version number.
If you need to rebuild and obfuscate the package without making any source changes though for some reason, you should navigate to the project directory. From there, delete the
obj folder. After doing this, Visual Studio will be forced to rebuild the package and you will not get this warning.
There's something else happening!?
You can also change a Visual Studio option so that MSBuild is more verbose about its messages. To set this option:
- Go to Tools in the menu bar.
- Go to Projects and Solutions. Expand it.
- Go to Build and Run.
- Change the "MSBuild project build output verbosity" so that it's something other than quiet. Normal is useful for debugging errors.
- Click OK.
Now, you should be able to look in the output tab to see exactly what the Obfuscate task executes and hopefully figure out what went wrong.
You should be able to see something like this: