How to obfuscate C# application in Visual Studio

A .NET obfuscator for Visual Studio

Contents

In this tutorial, we will show you how to obfuscate a .NET application in Visual Studio.

ArmDot is a famous .NET obfuscator. Visual Studio is a modern IDE for .NET developers from Microsoft.

Reasons to use obfuscators

Internals of .NET applications are open for everyone; an unobfuscated application can be even decompiled to C#, modified, and rebuilt! Without applying obfuscators, you nearly provide everyone with the source code of your product for free.

Obfuscators help developers hide the implementation details of their applications. They also encrypt and rename types and methods that would disclose the application’s logic.

By applying the techniques listed above, your .NET application will be protected from hackers and prying eyes who wish to crack your algorithms and extract assets like images and other resources.

ArmDot protects your code by making it virtually impossible to understand and exploit your software vulnerabilities.

Enable obfuscation in Visual Studio

The source code for this project is available on GitHub:
https://github.com/Softanics/armdot-visual-studio-sample

Start Visual Studio and create a new C# console application.

To install ArmDot.Engine.MSBuildTasks, right-click on the project in Visual Studio and select Manage NuGet Packages. Type “ArmDot.Engine.MSBuildTasks” into the search bar, right-click on it, and choose Install. When it is installed, Visual Studio will inform you. Do the same for “ArmDot.Client” as shown below:

Add .NET obfuscator to Visual Studio

ArmDot.Client contains obfuscation attributes to specify obfuscation options.

ArmDot.Engine.MSBuildTasks provides a task that MSBuild executes to run ArmDot.

ArmDot has been added to the project but not enabled yet. To add ArmDot to the building process, add a target executed after the project is built. This target should run the task ArmDot.Engine.MSBuildTasks.ObfuscateTask that takes just compiled assembly and obfuscate it.

Right-click to the project, select EditEdit project. Now you can edit the project file. Add the task as shown below:

<Project Sdk="Microsoft.NET.Sdk">

    <PropertyGroup>
        <OutputType>Exe</OutputType>
        <TargetFramework>net6.0</TargetFramework>
        <RootNamespace>armdot_visual_studio_sample</RootNamespace>
    </PropertyGroup>

    <Target Name="Protect" AfterTargets="Build">
        <ItemGroup>
            <Assemblies Include="$(TargetDir)$(TargetFileName)" />
        </ItemGroup>
        <ArmDot.Engine.MSBuildTasks.ObfuscateTask
          Inputs="@(Assemblies)"
          ReferencePaths="@(_ResolveAssemblyReferenceResolvedFiles->'%(RootDir)%(Directory)')" />
    </Target>

    <ItemGroup>
      <PackageReference Include="ArmDot.Client" Version="2021.17.0" />
      <PackageReference Include="ArmDot.Engine.MSBuildTasks" Version="2021.17.0" />
    </ItemGroup>
    
</Project>

ArmDot.Engine.MSBuildTasks.ObfuscateTask has several parameters; you can find more information here.

Inputs specifies assemblies to obfuscate. In this sample, the only assembly is obfuscated, and this path can be retrieved using $(TargetDir)$(TargetFileName).

ReferencePaths provides the list of directories where ArmDot searches for referenced assemblies. Sometimes ArmDot needs complete information for an external type used in your application. The provided list allows ArmDot to locate and read type information. If ArmDot has failed to find the type information, it outputs the error. In most cases, you don’t need to type paths manually, as the compiler needs the same directories; just use @(_ResolveAssemblyReferenceResolvedFiles->’%(RootDir)%(Directory)’) which contains directories collected during compilation.

To make sure that obfuscation is enabled, rebuild the project, you will see the following:

1>V:\Projects\armdot-visual-studio-sample\armdot-visual-studio-sample\armdot-visual-studio-sample.csproj(13,9): warning : [ArmDot] warning ARMDOT0003: No methods to protect in the assembly V:\Projects\armdot-visual-studio-sample\armdot-visual-studio-sample\bin\Debug\net6.0\armdot-visual-studio-sample.dll
1>[ArmDot] Writing protected assembly to V:\Projects\armdot-visual-studio-sample\armdot-visual-studio-sample\bin\Debug\net6.0\armdot-visual-studio-sample.dll...
1>[ArmDot] Finished
1>Done building project "armdot-visual-studio-sample.csproj".

ArmDot has done its job but unfortunately has found out that there are no methods to obfuscate. Indeed, we didn’t tell ArmDot what we wanted to obfuscate.

The project is empty now. Let’s add some code. Imagine the application asks for a password and displays whether it is correct or not. To check a password, it compares the password hash with the correct one. We will use ArmDot to obfuscate the application logic; we need it to prevent changing the valid hash value.

The code is the following:

static void Main(string[] args)
{
    Console.WriteLine("Enter password and press ENTER");

    if (CheckPassword(Console.ReadLine()))
        Console.WriteLine("The password is correct");
    else
        Console.WriteLine("The password is not correct");
}

static bool CheckPassword(string value)
{
    using (var sha256 = SHA256.Create())
    {
        byte[] hashValue = sha256.ComputeHash(Encoding.UTF8.GetBytes(value));
        return "mZfua8BSQJP337Kuj4Cpl9dVBL/S6Cn1SioM0xcq2tg=" == Convert.ToBase64String(hashValue);
    }
}

Build and run the project. Enter armdot and ensure that it is the correct password.

It is good time to show that non-obfuscated code is easy to read and modify. Open the Windows Start Menu, type Developer Command Prompt for VS 2022, and run it. Then run ildasm.exe, click to FileOpen, and choose armdot-visual-studio-sample.dll. As you can see, the code is open:

Without obfuscation, the code is easy to hack

It’s time to obfuscate! You can virtualize all the methods but adding the following code in any source file:

[assembly: ArmDot.Client.VirtualizeCode]

Rebuild the project. You will that ArmDot obfuscated the methods this time:

1>[ArmDot] ------ Build started: Assembly (1 of 1): armdot-visual-studio-sample.dll (V:\Projects\armdot-visual-studio-sample\armdot-visual-studio-sample\bin\Debug\net6.0\armdot-visual-studio-sample.dll) ------
1>[ArmDot] Conversion started for method System.Void armdot_visual_studio_sample.Program::Main(System.String[])
1>[ArmDot] Conversion finished for method System.Void armdot_visual_studio_sample.Program::Main(System.String[])
1>[ArmDot] Conversion started for method System.Boolean armdot_visual_studio_sample.Program::CheckPassword(System.String)
1>[ArmDot] Conversion finished for method System.Boolean armdot_visual_studio_sample.Program::CheckPassword(System.String)
1>[ArmDot] Conversion started for method System.Void armdot_visual_studio_sample.Program::.ctor()
1>[ArmDot] Conversion finished for method System.Void armdot_visual_studio_sample.Program::.ctor()
1>[ArmDot] Writing protected assembly to V:\Projects\armdot-visual-studio-sample\armdot-visual-studio-sample\bin\Debug\net6.0\armdot-visual-studio-sample.dll...
1>[ArmDot] Finished

Check if the application still works correctly: run and enter armdot.

But how does the obfuscated code look like now? Well, run ildasm.exe once again and check. It is just a mess of thousands of low-level instructions:

Obfuscated code is hard to hack

Summary

.NET-based projects are trendy nowadays, but nobody wants to distribute their application with the complete source code. Thanks to modern obfuscation techniques like code virtualization, you can convert any .NET application to a messy set of .NET instructions.

A fully-functional ArmDot demo is available on the downloading page.