Protect Your App
Protecting your entire .NET or Xamarin app is as simple as adding a few lines to the app's project file (e.g., MyApp.csproj
, MyApp.vbproj
).
Once integrated, Dotfuscator Professional will, by default, protect all of your assemblies - whether from the app's project or other projects in your solution - automatically, with every Release
build.
(There are additional settings to exclude projects or include other assemblies.)
Integrate into Your App's Project File
To integrate Dotfuscator into a project, edit the project file (.csproj
, .vbproj
, etc.) and make the changes shown below.
If you are using Visual Studio (for Windows or Mac), you may have to unload the project and reload it after making changes.
Only make these changes to your main project (which is probably your startup project).
.NET Framework
To protect a .NET Framework project, copy the new XML elements shown below (<PropertyGroup>
, etc.) and paste them into your project file immediately before the closing </Project>
tag.
Note that the order of the elements is important.
If you are using an SDK-style project see the .NET Core instructions.
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Build">
<!-- ...existing tags... -->
<!-- Import environment-specific build properties for Dotfuscator, if they exist. -->
<Import Project="$([System.Environment]::GetFolderPath(SpecialFolder.UserProfile))\.dotfuscator.user.props"
Condition="Exists('$([System.Environment]::GetFolderPath(SpecialFolder.UserProfile))\.dotfuscator.user.props')"/>
<!-- Set build properties for Dotfuscator. -->
<PropertyGroup>
<!-- Specify the location of the MSBuild targets, if not already provided. -->
<DotfuscatorMSBuildDir Condition="'$(DotfuscatorMSBuildDir)' == ''">$(MSBuildProgramFiles32)\MSBuild\PreEmptive\Dotfuscator\6</DotfuscatorMSBuildDir>
<!-- Generate a default Dotfuscator config file (DotfuscatorConfig.xml) if it doesn't exist. -->
<!-- TODO: Set this to false after the file is generated by the first local build. -->
<DotfuscatorGenerateConfigFileIfMissing>true</DotfuscatorGenerateConfigFileIfMissing>
<!-- Enable Dotfuscator for Release builds. -->
<DotfuscatorEnabled Condition="'$(Configuration)' == 'Release'">true</DotfuscatorEnabled>
</PropertyGroup>
<!-- Import the Dotfuscator MSBuild targets. Must be done last. -->
<Import Project="$(DotfuscatorMSBuildDir)\PreEmptive.Dotfuscator.Common.targets" />
</Project>
.NET Core or .NET Standard
By default, .NET Core and .NET Standard projects use SDK-style projects. To protect an SDK-style project, you must ensure that the Dotfuscator targets file is imported last. This can be done by using explicit SDK imports.
First remove the Sdk
attribute from the project's root <Project>
tag.
Then, copy the Import
tag shown below (replacing the SDK name if necessary) into the appropriate position in your project file.
<!-- ORIGINALLY WAS: <Project Sdk="Microsoft.NET.Sdk"> OR <Project Sdk="Microsoft.NET.Sdk.WindowsDesktop"> OR similar -->
<Project>
<!-- The Sdk attribute has been replaced with explicit <Import> tags to ensure Dotfuscator's targets are imported after "Sdk.targets" -->
<!-- Import SDK properties (before any existing tags) -->
<!-- TODO: UPDATE THE "Sdk" ATTRIBUTE TO MATCH THE VALUE ORIGINALLY IN THE <Project> TAG -->
<Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk" />
<!-- ...existing tags... -->
Then, copy the new elements shown below (replacing the SDK name if necessary) into the appropriate positions in your project file.
<!-- ...existing tags... -->
<!-- Import SDK targets (after any existing tags but before Dotfuscator targets) -->
<!-- TODO: UPDATE THE "Sdk" ATTRIBUTE TO MATCH THE VALUE ORIGINALLY IN THE <Project> TAG -->
<Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" />
<!-- Import environment-specific build properties for Dotfuscator, if they exist. -->
<Import Project="$([System.Environment]::GetFolderPath(SpecialFolder.UserProfile))\.dotfuscator.user.props"
Condition="Exists('$([System.Environment]::GetFolderPath(SpecialFolder.UserProfile))\.dotfuscator.user.props')"/>
<!-- Set build properties for Dotfuscator. -->
<PropertyGroup>
<!-- Specify the location of the MSBuild targets, if not already provided. -->
<DotfuscatorMSBuildDir Condition="'$(DotfuscatorMSBuildDir)' == ''">$(MSBuildProgramFiles32)\MSBuild\PreEmptive\Dotfuscator\6</DotfuscatorMSBuildDir>
<!-- Generate a default Dotfuscator config file (DotfuscatorConfig.xml) if it doesn't exist. -->
<!-- TODO: Set this to false after the file is generated by the first local build. -->
<DotfuscatorGenerateConfigFileIfMissing>true</DotfuscatorGenerateConfigFileIfMissing>
<!-- Enable Dotfuscator for Release builds. -->
<DotfuscatorEnabled Condition="'$(Configuration)' == 'Release'">true</DotfuscatorEnabled>
</PropertyGroup>
<!-- Import the Dotfuscator MSBuild targets. Must be done last. -->
<Import Project="$(DotfuscatorMSBuildDir)\PreEmptive.Dotfuscator.Common.targets" />
</Project>
Xamarin
For full details on Dotfuscator's support of Xamarin, please see the Xamarin page.
If your app targets multiple platforms (i.e., both Android and iOS), you must integrate Dotfuscator into each of the corresponding output projects.
To protect a Xamarin project (we suggest starting with Android), copy the new elements shown below into the appropriate positions in your project file immediately before the closing </Project>
tag.
Note that the order of the elements is important.
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- ...existing tags... -->
<!-- Import environment-specific build properties for Dotfuscator, if they exist. -->
<Import Project="$([System.Environment]::GetFolderPath(SpecialFolder.UserProfile))\.dotfuscator.user.props"
Condition="Exists('$([System.Environment]::GetFolderPath(SpecialFolder.UserProfile))\.dotfuscator.user.props')"/>
<!-- Set build properties for Dotfuscator. -->
<PropertyGroup>
<!-- Specify the location of the MSBuild targets, if not already provided. -->
<DotfuscatorMSBuildDir Condition="'$(DotfuscatorMSBuildDir)' == ''">$(MSBuildProgramFiles32)\MSBuild\PreEmptive\Dotfuscator\6</DotfuscatorMSBuildDir>
<!-- Generate a default Dotfuscator config file (DotfuscatorConfig.xml) if it doesn't exist. -->
<!-- TODO: Set this to false after the file is generated by the first local build. -->
<DotfuscatorGenerateConfigFileIfMissing>true</DotfuscatorGenerateConfigFileIfMissing>
<!-- Enable Dotfuscator for Release builds. -->
<DotfuscatorEnabled Condition="'$(Configuration)' == 'Release'">true</DotfuscatorEnabled>
<!-- Enable Dotfuscator for Ad-Hoc builds (only needed for iOS). -->
<DotfuscatorEnabled Condition="'$(Configuration)' == 'Ad-Hoc'">true</DotfuscatorEnabled>
<!-- Enable Dotfuscator for AppStore builds (only needed for iOS). -->
<DotfuscatorEnabled Condition="'$(Configuration)' == 'AppStore'">true</DotfuscatorEnabled>
<!-- Only needed when using Tamper Checks for Android. -->
<!-- TODO: If using Tamper Checks for Android, set this to the SHA-1 fingerprint of the certificate used to sign the app. -->
<DotfuscatorAndroidSigningCertFingerprint></DotfuscatorAndroidSigningCertFingerprint>
</PropertyGroup>
<!-- Import the Dotfuscator MSBuild targets. Must be done last. -->
<Import Project="$(DotfuscatorMSBuildDir)\PreEmptive.Dotfuscator.Common.targets" />
</Project>
Build the Project
To get a protected app, build the project just as you normally would, in the Release
configuration.
Dotfuscator supports building via any MSBuild-based tool, including Visual Studio (for Windows or Mac), Visual Studio Code, and the .NET Core command line (dotnet
).
The screenshot below shows Visual Studio for Windows.
As part of this initial build, Dotfuscator will generate a config file, DotfuscatorConfig.xml
, with default protection settings.
The build will emit a warning to this effect (seen in the screenshot above), which you can ignore for this first build.
Check the generated file into version control.
The build will then call Dotfuscator to protect the solution's assemblies (.exe
and .dll
files) in the project's output directory (e.g., bin\Release
).
Dotfuscator will also produce report files in a new DotfuscatorReports
directory; you should exclude this directory from version control.
Once the build completes, congratulations! Your app is now protected by Dotfuscator!
Disable Config File Generation
During the first build, Dotfuscator generated a config file, DotfuscatorConfig.xml
, with default protection settings.
This feature is helpful when getting set up, but once the file exists (and is tracked by version control) you should disable this feature because it can mask a certain kind of build error.
To disable config file generation, edit your project file (e.g., .csproj
) again and replace the following lines:
<!-- Generate a default Dotfuscator config file (DotfuscatorConfig.xml) if it doesn't exist. -->
<!-- TODO: Set this to false after the file is generated by the first local build. -->
<DotfuscatorGenerateConfigFileIfMissing>true</DotfuscatorGenerateConfigFileIfMissing>
with:
<!-- Error if the Dotfuscator config file (DotfuscatorConfig.xml) is missing. -->
<DotfuscatorGenerateConfigFileIfMissing>false</DotfuscatorGenerateConfigFileIfMissing>
Examine the Protected Assemblies
After integrating Dotfuscator into your project, you should verify that the integration is operating correctly. You might also be curious about what kind of protection Dotfuscator is applying by default.
The easiest way to answer these questions is to use reverse engineering tools on your project's assemblies, decompiling them back into high-level C# code.
You can decompile assemblies built locally (e.g., in bin\Release
) as well as those laid down by your app's installer.
For full details of how to decompile assemblies, see Decompiling.
As an example, consider decompiling a method in the GettingStarted
sample app before and after integrating Dotfuscator:
Unprotected | Default protection (excerpt) |
---|---|
We can clearly see what the unprotected method is doing, as well as its name, just as if we had the source code.
However, with Dotfuscator's default protection, the simple for
loop has been converted into a confusing mess of switch
and goto
statements by Control Flow obfuscation.
Additionally, the name of the method and its defining type have been replaced with short, meaningless names thanks to Renaming obfuscation.
Note that this is just the default protection Dotfuscator provides. With some additional configuration, Dotfuscator can cause decompilation tools to outright crash when processing your assemblies:
Default protection (excerpt) | Enhanced protection |
---|---|
Dotfuscator can also inject Checks into your app, which detect and respond to unauthorized usage at runtime. For instance, a Debugging Check can detect if a debugger is attached to your production app and, if so, terminate the app.
For details on configuring these and other stronger forms of protection, see Enhance Protection.
Build on Build Agents
In addition to local builds, Dotfuscator also protects your app during automated builds. For details on setting up Dotfuscator on your build agents, see the documentation for Build Agents.
Archive Report Files
As part of the build, Dotfuscator produces report files (in the DotfuscatorReports
directory).
These reports contain information that can be useful when testing, releasing, and supporting a protected app.
For instance, the renaming map file (Renaming.xml
) enables you to decode obfuscated stack traces produced by the app.
You should archive these reports, especially for builds you release. This way, if you later run into an issue with a certain version of your app, you will have the corresponding report files to assist you.
If your team uses a continuous integration and delivery (CI/CD) pipeline or other automated build system, configure it to archive the contents of the DotfuscatorReports
after every build.
Otherwise, make a note in your release process or checklist to manually archive this directory when releasing the app.
Be sure to store the reports in a secure, versioned location so you can refer to them later.
Enhance Protection
Dotfuscator offers default protection settings when you first integrate it into a .NET or Xamarin project, as demonstrated earlier. These settings are chosen to give your app reasonably-strong protection without requiring you to perform additional configuration, and to reduce the risk of protection interfering with your app's normal operation.
However, Dotfuscator offers much stronger protection than these defaults. For details, see the Enhance Protection page.
Alternative Approaches
This page demonstrated a recommended approach for using Dotfuscator, where the protection is applied by Dotfuscator's MSBuild targets. For some scenarios, this approach may not be suitable. An alternative approach may be better if any of the following are true:
- You are using a custom MSBuild project that uses neither the "Common" .NET Framework targets (e.g.,
Microsoft.CSharp.targets
) nor a .NET Core SDK (e.g.,Microsoft.NET.Sdk
). - You need to use Dotfuscator's Linking feature.
- You need Dotfuscator to run after the MSBuild packaging steps.
- Your project is built by a build system that isn't based on MSBuild.
- You don't have access to the project itself, just the already-compiled assemblies (i.e.,
.exe
and.dll
files).
In these cases, you will need to have Dotfuscator run after the normal compilation step to create protected versions of the assemblies (.exe
and .dll
files) that were compiled.
The general procedure is as follows:
Create a Config File which specifies the assemblies Dotfuscator will protect and where the protected versions will be written.
On Windows, you can use the graphical Config Editor to create and test your configuration locally. For details, see Working with Configs.
On all platforms, you can use the command line interface to create and test your configuration locally. For details, see Calling the Command Line and Saving a Config File from the Command Line.
You can also create a Config File using a text editor; refer to the Config File reference documentation.
If you use an automated build system, install Dotfuscator onto your build agents.
Call Dotfuscator during your build, after your build system has project has compiled your code into .NET assemblies. You will need to pass the path to the Config File you created as an argument to Dotfuscator.
There are a number of ways to call Dotfuscator, listed below.
The
Dotfuscate
MSBuild task. This is a good choice if you are familiar with MSBuild and have customized a .NET project file (e.g.,.csproj
) to have additional targets or are running a separate MSBuild project.For instance, you can run Dotfuscator after the
Build
target:<!-- Import environment-specific properties, which may include DotfuscatorMSBuildDir --> <Import Project="$([System.Environment]::GetFolderPath(SpecialFolder.UserProfile))\.dotfuscator.user.props" Condition="Exists('$([System.Environment]::GetFolderPath(SpecialFolder.UserProfile))\.dotfuscator.user.props')"/> <!-- Set a default value for the DotfuscatorMSBuildDir property --> <PropertyGroup> <DotfuscatorMSBuildDir Condition="'$(DotfuscatorMSBuildDir)' == ''">$(MSBuildProgramFiles32)\MSBuild\PreEmptive\Dotfuscator\6</DotfuscatorMSBuildDir> </PropertyGroup> <!-- Reference the Dotfuscate task using the DotfuscatorMSBuildDir property --> <UsingTask TaskName="PreEmptive.Tasks.Dotfuscate" AssemblyFile="$(DotfuscatorMSBuildDir)\PreEmptive.Dotfuscator.Tasks.dll" /> <!-- Use the Dotfuscate task --> <Target Name="MyTarget" AfterTargets="Build"> <Dotfuscate ConfigPath="path\to\your\DotfuscatorConfig.xml" License="$(DotfuscatorLicense)" /> </Target>
Dotfuscator's Command Line Interface. This can be called from any script or build system which can execute regular command line tools, so it's a good fit for all scenarios that Dotfuscator supports. The way you call the tool depends on how you installed Dotfuscator.
If you used the Windows installer (
.msi
),dotfuscator
is available as a .NET Framework command line app in the directory specified by theDOTFUSCATOR_HOME
environment variable. You can call it like any Windows program:"%DOTFUSCATOR_HOME%\dotfuscator.exe" path\to\your\DotfuscatorConfig.xml
If you used the NuGet package, the command line interface is a .NET Core app. You must have the .NET Core 2.1 runtime or later installed; then you can run the command line with the
dotnet
command:dotnet <nuget install dir>/PreEmptive.Protection.Dotfuscator.Pro/tools/programdir/netcore/dotfuscator.dll path/to/your/DotfuscatorConfig.xml
or by using one of the scripts in the install directory:
<nuget install dir>/PreEmptive.Protection.Dotfuscator.Pro/tools/programdir/netcore/dotfuscator path/to/your/DotfuscatorConfig.xml
Note: The NuGet installation instructions only had you activate the MSBuild components. To use the command line interface as provisioned by the NuGet package, you must activate Dotfuscator separately, either with an environment variable or by passing your license string through the command line's/license
or-license
argument.
The Dotfuscator Azure Pipelines Extension. This extension, available in the Visual Studio Marketplace, can be installed into an Azure Pipelines organization to provide the Dotfuscator Professional Command Line task. You can then add the task to your pipeline in the UI and specify the path to your config file in the Path To Config File field. Or, if you use the YAML pipeline syntax, here's an example of an added Dotfuscator step:
- task: DotfuscatorCLI@1 displayName: 'Dotfuscator Professional Command Line using DotfuscatorConfig.xml' inputs: configFile: DotfuscatorConfig.xml
You will need to preserve the report files Dotfuscator produces, at least with every release, but preferably with every build. If your team uses an automated build system, configure it to archive the reports after every build. Otherwise, make a note in your release process or checklist to manually archive this directory when releasing the app. Be sure to store the reports in a secure, versioned location so you can refer to them later.