If you are still using Dotfuscator v6, please indicate the installed product version for the targets path.
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, by default, protecta 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).
Modern .NET projects use SDK-style projects by default. To protect an SDK-style project, ensure that the Dotfuscator targets file is imported last. You can do this by using explicit SDK imports.
- Remove the
Sdkattribute from the project's root<Project>tag. -
Copy the
Importtag 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... --> -
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/7</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>
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/7</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>
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/7</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. -->
<!-- If you are using key rotation and your application requires Android Pie (or later), use the last alias in the rotation, otherwise, you need to provide the first alias in the rotation. -->
<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 generates a config file, DotfuscatorConfig.xml, with default protection settings. The build emits 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 then calls Dotfuscator to protect the solution's assemblies (.exe and .dll files) in the project's output directory (e.g., bin/Release). Dotfuscator also produces report files in a new DotfuscatorReports directory; exclude this directory from version control.
Once the build completes, congratulations! Your app is now protected by Dotfuscator!
Before releasing your protected app or library, review the Release Checklist. It gathers in one place all of the topics that you should consider when releasing your protected software.
Disable Config File Generation
During the first build, Dotfuscator generates 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, 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) |
|---|---|
You can clearly see what the unprotected method does, as well as its name, just as if you had the source code. However, with Dotfuscator's default protection, Control Flow obfuscation converts the simple for loop into a confusing mess of switch and goto statements. Additionally, Renaming obfuscation replaces the name of the method and its defining type with short, meaningless names.
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 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 demonstrates 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.,
.exeand.dllfiles).
In these cases, you 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 protects and where it writes the protected versions.
- 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 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
DotfuscateMSBuild 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
Buildtarget:<!-- 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/7</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. You can call this from any script or build system executes 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),dotfuscatoris available as a .NET Framework command line app in the directory specified by theDOTFUSCATOR_HOMEenvironment 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 6 runtime or later installed; then you can run the command line with the
dotnetcommand:dotnet <nuget install dir>/PreEmptive.Protection.Dotfuscator.Pro/tools/programdir/netcore/dotfuscator.dll path/to/your/DotfuscatorConfig.xmlor 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
-
-
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 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.