UWP Applications in Dotfuscator CE

Dotfuscator Community Edition (CE) has long been a staple of the Visual Studio experience, providing industry-standard reverse-engineering protection to .NET applications. With the release of Visual Studio 2015 Update 3, we’re extending this pedigree to applications for the Universal Windows Platform (UWP), also known as universal apps. This blog post will show you how to integrate Dotfuscator CE into the Visual Studio build process for UWP applications.

Page Contents

Prerequisites

Before we begin, there are a few things you will need in order to build UWP apps and protect them with Dotfuscator. These instructions assume you already have Visual Studio installed with the capability of building UWP apps.

In Visual Studio 2015

Support for UWP apps in Dotfuscator CE began with Visual Studio 2015 Update 3. If you are using an earlier version of Visual Studio, you will need to upgrade. As part of the Visual Studio 2015 upgrade, Dotfuscator CE will automatically be updated to version 5.22.0, ready to support UWP apps.

In Visual Studio 2017

The version of Dotfuscator CE included with Visual Studio 2017 already supports UWP apps. You may already have Dotfuscator CE installed; search for dotfuscator in Visual Studio 2017’s Quick Launch (Ctrl+Q):

  • If Dotfuscator CE is already installed, this will bring up the Menu option to start the Dotfuscator CE user interface.
  • If Dotfuscator CE is not yet installed, this will bring up the relevant Install option. See these Installation instructions for details.

Windows 10 SDK

Note: This section is only required if you are using Dotfuscator CE’s Checks or Analytics.

Dotfuscator CE’s Checks and Analytics features currently only support Windows 10 SDK version 10.0.10240. Thus, if you wish to use this feature, you must set this version as the Min version in your application’s projects

  1. In Visual Studio, right-click on the project and select Properties.
  2. In the Application page, under the Targeting section, set Min version to Windows 10 (10.0; Build 10240).

If this option does not appear, you will need to install this version of the Windows 10 SDK:

  • In Visual Studio 2015:
    1. Open the Windows 10 Programs and Features window (found by right-clicking the Start button).
    2. Select Microsoft Visual Studio [Edition Name] 2015.
    3. Click Change.
    4. On the dialog that appears, click Modify.
    5. Within the Features tree, locate the Windows and Web Development -> Universal Windows App Development Tools section.
    6. Check the Windows 10 SDK (10.0.10240) node.
    7. Click Next and then continue through the setup process.
  • In Visual Studio 2017:
    1. Open the Start Menu.
    2. Select All apps.
    3. From the list of apps, select Visual Studio Installer.
    4. The Visual Studio installer opens. Under Installed, locate the edition of Visual Studio you are using, and click Modify.
    5. Select the Individual components tab.
    6. Under SDKs, libraries, and frameworks, check the Windows 10 SDK (10.0.10240.0) item.
    7. Click Modify and then continue the setup process.

Dotfuscator CE Registration

Integrating Dotfuscator CE with the build process requires use of the command-line interface, which is only available in registered copies of CE. If you have not already registered CE:

  1. Open Visual Studio.
  2. Open Dotfuscator CE via Tools -> PreEmptive Protection – Dotfuscator.
  3. Start the registration process via Help -> Register Product…
    • Note: If this is the first time Dotfuscator CE has been opened, you will need to accept the End-User License Agreement to continue. Then, the registration dialog will appear automatically.

Creating a Dotfuscator Project

To start, let’s create a project in Dotfuscator CE for the relevant assemblies from your application.

Building the Debug Configuration

We first need a set of unobfuscated assemblies and markup for Dotfuscator to take as input. We will use the output of the Debugconfiguration as our input when configuring Dotfuscator, though our final integration with Visual Studio will use the Releaseconfigurations instead.

To build the inputs needed for Dotfuscator:

  1. Open your solution in Visual Studio.
  2. Select Debug from the Solution Configurations drop-down.
  3. Select an appropriate processor architecture from the Solution Platforms drop-down.
  4. Build the application.
ceuwp InitialDebugBuild

For the next sections, we need to know the paths to these files. To locate the built files after the build is complete:

  1. From Solution Explorer, right-click on your startup project and select Open Folder in File Explorer.
  2. Windows Explorer opens the directory holding the Visual Studio project file (e.g., .csproj) for your startup project. We will refer to this as the project directory.
  3. Navigate to the subdirectory bin, then the appropriate subdirectory for your built platform and configuration (e.g., binx64Debug). We will refer to this as the debug build directory.

Adding Inputs

In Visual Studio, open Dotfuscator CE via Tools -> PreEmptive Protection – Dotfuscator.

Add the inputs to your new Dotfuscator project:

  1. On Dotfuscator CE’s Inputs page, click the Add Input button ( ceuwp AddAssemblyButton 1).
  2. In the dialog that appears, navigate to the debug build directory. The File name field should still have the value Folder Select.
  3. Click Open to add the entire directory to your Dotfuscator project.
  4. Save the Dotfuscator project as Dotfuscator.xml in your project directory.
ceuwp AddingInputs

Use Project Properties

Because UWP apps are built for multiple processor architectures, we don’t want to hardcode our inputs to draw from a specific directory on-disk. Instead, we will use Dotfuscator project properties to stand in for the path to the input directory. This way, we only have to make one Dotfuscator project for all architectures the app will support.

Dotfuscator automatically defines a project property, configdir, representing the absolute path to the project directory (in general, the directory where the Dotfuscator project is saved).

We will additionally define a builddir property, representing the relative path from the project directory to the build directory. We will use the debug build directory as our default value for this new property. When we integrate Dotfuscator with Visual Studio, we will set this property to the appropriate path for each Release build – i.e., the release build directory.

To set up the builddir project property:

  1. On Dotfuscator CE’s Properties page, select the Project Properties tab.
  2. Under the Project properties table, double-click the first empty row’s Property cell. Enter the property name builddir.
  3. Double-click the corresponding Value cell. Enter the relative path from the project directory to the debug build directory (e.g., binx64Debug).
ceuwp UseProjectProperties1

To use the project properties in the input path:

  1. Return to the Inputs page and double-click the name of the package. An editable text field appears in its place.
  2. Replace the contents of the field with ${configdir}${builddir}.
  3. Save the Dotfuscator project.
ceuwp UseProjectProperties2

Now, we will be able to supply an argument to Dotfuscator CE’s command-line interface to change what assemblies Dotfuscator will process. But because we defined a the Debug configuration’s output path as the default value for this project property, we can use those unobfuscated assemblies as a baseline for configuring the Dotfuscator project.

Disabling Transforms Temporarily

Before we integrate our project with Visual Studio, we should temporarily disable the protection and analytics transforms done by Dotfuscator CE. This will allow us to verify that our integration is correct before introducing additional complexities with the core features of Dotfuscator.

To disable transforms:

  1. Right-click on the Analytics node, then uncheck Enable.
  2. Right-click on the Renaming node, then uncheck Enable.
  3. Save the Dotfuscator project.

Integrate with Visual Studio

Now that we have our Dotfuscator project set up, we need to have Visual Studio run Dotfuscator CE during its build process. Specifically, we will be adding an MSBuild Target to the Visual Studio project file for the startup project.

Edit the Visual Studio Project

To begin editing the Visual Studio project:

  1. Open your solution in Visual Studio if you have not already done so.
  2. From Solution Explorer, right-click on your startup project and select Unload Project.
  3. Right-click on the now-unloaded startup project and select Edit [Project’s Name].csproj.

Visual Studio will open a text editor for the project file (e.g., YourStartupProject.csproj).

Create a Target

Under the <Project> element, locate the following line:

<Import Project="$(MSBuildExtensionsPath)MicrosoftWindowsXamlv$(VisualStudioVersion)Microsoft.Windows.UI.Xaml.CSharp.targets" />

Immediately below that line, insert the following:

<Target Name="AfterGenerateAppxManifest" Condition="'$(Configuration)' != 'Debug'">
</Target>

This creates an MSBuild target that Visual Studio will call while building your UWP app. Specifically, it is called after the assemblies have been created but before they have been packaged or compiled with .NET Native. The exact name AfterGenerateAppxManifest must be used. Otherwise, Visual Studio will not call this target at the appropriate time.

Note also the target’s Condition attribute. We prevent use of this target during Debug builds so that such builds are not obfuscated, and thus easier to debug.

Set Build Properties

For convenience, we will define a number of MSBuild properties to store paths we will be using in the following steps. Within the target we created, add a <PropertyGroup> similar to the following:

<PropertyGroup>
  <DotfuscatorCLIPath>$(DevEnvDir)....PreEmptive SolutionsDotfuscator and Analytics Community EditionDotfuscatorCLI.exe</DotfuscatorCLIPath>
  <DotfuscatorBuildDir>$(OutDir)</DotfuscatorBuildDir>
  <DotfuscatorConfigPath>$(ProjectDir)Dotfuscator.xml</DotfuscatorConfigPath>
  <DotfuscatorOutputDir>$(ProjectDir)Obfuscated</DotfuscatorOutputDir>
  <DotfuscatorOutputMapPath>$(ProjectDir)ObfuscationMapsRenaming.xml</DotfuscatorOutputMapPath>
</PropertyGroup>

Where:

  • DotfuscatorCLIPath is the path to the Dotfuscator CE CLI executable (located in Visual Studio’s install directory).
  • DotfuscatorBuildDir is the path to the release build directory. In this case, we just re-use the value of OutDir, the output directory of the Visual Studio build. This property will change depending on which processor architecture Visual Studio is building, directing Dotfuscator to the appropriate unobfuscated assemblies to take as input.
  • DotfuscatorConfigPath is the path to the project file we created for Dotfuscator CE.
  • DotfuscatorOutputDir is the path to a directory where obfuscated assemblies will be placed. We’ll refer to this as the obfuscation output directory.
  • DotfuscatorOutputMapPath is the path to an XML file that will contain information about how Dotfuscator CE renamed symbols within the processed assemblies. This is useful for understanding stack traces in obfuscated versions of your application. Each execution of Dotfuscator will create a new map file, renaming any existing ones.

Run Dotfuscator

Next, add an <Exec> element after the <PropertyGroup>:

<Exec Command="&quot;$(DotfuscatorCLIPath)&quot; /p=builddir=$(DotfuscatorBuildDir) /out=&quot;$(DotfuscatorOutputDir)&quot; /mapoutput=&quot;$(DotfuscatorOutputMapPath)&quot; &quot;$(DotfuscatorConfigPath)&quot;" />

This will call Dotfuscator CE’s command-line interface with the following arguments:

  • /p=builddir=$(DotfuscatorBuildDir) sets the builddir project property we defined in our Dotfuscator project to appropriate release build directory.
  • /out=&quot;$(DotfuscatorOutputDir)&quot; causes Dotfuscator CE to write transformed assemblies to the obfuscation output directory.
  • /mapoutput=&quot;$(DotfuscatorOutputMapPath)&quot; causes Dotfuscator CE to write information about renamed symbols to the appropriate XML file.
  • &quot;$(DotfuscatorConfigPath)&quot; causes Dotfuscator CE to use all other settings we specified in our Dotfuscator project file.

Note that we surround various paths with quotation marks – encoded in the XML format as &quot; – to handle cases where they contain spaces.

Copy Assemblies Back

After Dotfuscator CE processes the assemblies, our MSBuild target will need to copy the contents of the obfuscation output directory back to the release build directory, so that the rest of the build pipeline can continue with the obfuscated assemblies and markup.

Add the following after the <Exec> element:

<ItemGroup>
  <ObfuscatedFiles Include="$(DotfuscatorOutputDir)*.*" />
</ItemGroup>
<Copy SourceFiles="@(ObfuscatedFiles)" DestinationFolder="$(DotfuscatorBuildDir)" />

Load the Modified Project

At this point, the changes to the Visual Studio project file are complete. For reference, a complete target might look like this:

<Target Name="AfterGenerateAppxManifest" Condition="'$(Configuration)' != 'Debug'">
  <PropertyGroup>
    <DotfuscatorCLIPath>$(DevEnvDir)....PreEmptive SolutionsDotfuscator and Analytics Community EditionDotfuscatorCLI.exe</DotfuscatorCLIPath>
    <DotfuscatorBuildDir>$(OutDir)</DotfuscatorBuildDir>
    <DotfuscatorConfigPath>$(ProjectDir)Dotfuscator.xml</DotfuscatorConfigPath>
    <DotfuscatorOutputDir>$(ProjectDir)Obfuscated</DotfuscatorOutputDir>
    <DotfuscatorOutputMapPath>$(ProjectDir)ObfuscationMapsRenaming.xml</DotfuscatorOutputMapPath>
  </PropertyGroup>
  <Exec Command="&quot;$(DotfuscatorCLIPath)&quot; /p=builddir=$(DotfuscatorBuildDir) /out=&quot;$(DotfuscatorOutputDir)&quot; /mapoutput=&quot;$(DotfuscatorOutputMapPath)&quot; &quot;$(DotfuscatorConfigPath)&quot;" />
  <ItemGroup>
    <ObfuscatedFiles Include="$(DotfuscatorOutputDir)*.*" />
  </ItemGroup>
  <Copy SourceFiles="@(ObfuscatedFiles)" DestinationFolder="$(DotfuscatorBuildDir)" />
</Target>

Now, reload the project in Visual Studio:

  1. Save the project file.
  2. Right-click on the unloaded startup project and select Reload Project.
  3. On the dialog that asks if you want to close the project file before opening the project, say Yes.

Building

Now that the Visual Studio project has been modified, we can build the project in Visual Studio and have Dotfuscator automatically process the built assemblies and markup.

Build Without Transforms

First, we want to verify our integration is working at the simplest level before we re-enable obfuscation and analytics transforms (i.e., the real work that Dotfuscator does). To do this:

  1. Set MSBuild project build output verbosity to at least Normal.
    • In Visual Studio, this setting is found in Tools -> Options -> Projects and Solution ‒ Build and Run.
  2. Select Release from the Solution Configurations drop-down.
  3. Rebuild the application.
    • Rebuilding is necessary because Visual Studio does not track Dotfuscator project file changes in order to determine if an assembly is already up-to-date. In the future, if you make code changes since your last build, you can typically Build the application instead.
  4. Verify Dotfuscator ran as part of the build by locating text like the following within the Output pane.
ceuwp BuildWithoutTransforms

Note that if you instead rebuild in a Debug configuration, Dotfuscator will not run. This is because we prevented our added target from running in that case.

Re-Enable Transforms

Now that we’ve verified that Dotfuscator has successfully been integrated into Visual Studio’s build pipeline, we need to enable Dotfuscator’s core features: code protection and analytics instrumentation.

To enable transforms:

  1. Return to Dotfuscator CE and load the Dotfuscator project.
  2. Right-click on the Analytics node, then check Enable.
  3. Right-click on the Renaming node, then check Enable.
  4. Save the project.

Then, rebuild your application in a Release configuration. This build will now be obfuscated with Dotfuscator CE’s symbol-renaming algorithms.

Configure Renaming Exclusions and Analytics

Test your application after it’s been obfuscated. While Dotfuscator recognizes many common code patterns, certain scenarios may require some symbols to be excluded from renaming in order to ensure proper application function.

Additionally, if you are working with PreEmptive Analytics, Dotfuscator can inject the appropriate client-side analytics code into your application. (Note that your application’s minimum runtime version must be Windows 10 (10.0; Build 10240) in order to use this feature).

Both renaming exclusions and analytics attributes can be configured from the the Dotfuscator CE GUI, under the Renaming and Analytics nodes, respectively. For more information, refer to the Configuration Options pages of the Dotfuscator CE help.

After configuring and saving the Dotfuscator project, you will need to Rebuild your application in a Release configuration for the new rules to take effect.

Note: Recall that when configuring via the Dotfuscator CE GUI, the contents of the debug build directory are used as input. Before you use the GUI, you should make sure the Debugbuild selected earlier is built and up-to-date with respect to your current code.

Creating Application Packages

Once you have configured obfuscation for your application, you can package it for testing and release from the Project -> Store -> Create App Packages… option in Visual Studio.

Note the first question the wizard asks: “Do you want to build packages to upload to the Windows Store?”

  • Answering “No” will typically not produce an obfuscated build, because the application is built using Debug configurations.
  • Answering “Yes” will produce an obfuscated build, because the application is built using Release configurations. This includes both the packages designed for upload to the Windows Store as well as the sideloading packages for local testing.

Further Reading

While this blog post focused on Dotfuscator Community Edition (CE), we’ve also written a blog post on using Dotfuscator Professional Edition to protect UWP apps. One notable difference comes from Professional Edition’s included DotfuscateMSBuild task, which allows even easier integration into Visual Studio.

Even if you’re not using Professional Edition, you may still want to check out the linked post. It details an alternative UWP obfuscation method, where Visual Studio builds the application normally, and Dotfuscator acts on the packaged .appx files. Dotfuscator CE also works with this UWP package format, but we felt it would be more common for developers to use the Visual Studio integration method we’ve outlined in this post.

Please also take note of Community Edition’s specific licensing restrictions. The Dotfuscator CE license expressly prohibits the use of Dotfuscator CE by commercial organizations for anything other than personal research and education. If you would like to use Dotfuscator on commercial projects, please consider evaluating Dotfuscator Professional Edition.