.NET obfuscator for Linux

How to obfuscate a C# application on Linux?

Contents

.NET obfuscator for Linux

Initially, applications built for .NET could run on Windows only. Developed as an alternative to Java, the .NET runtime was unable to run on Linux for years. In 2014, Microsoft finally announced .NET Core, a version of .NET Runtime that is a cross-platform. Since it started working on Linux and macOS, having a .NET obfuscator that supports Linux has become an absolute must for software vendors.

This tutorial will demonstrate how to create a simple C# console application on Ubuntu and add obfuscation to the building process.

Creating a console application to obfuscate

First of all, check if dotnet is installed; type dotnet –versions. You should get the list of installed .NET runtimes:

razin@razin-VirtualBox:~/Desktop$ dotnet --version
6.0.100-rc.2.21505.57

If dotnet is not found, install .NET on Ubuntu as shown here.

Let’s create a console application using the command dotnet new console:

razin@razin-VirtualBox:~/projects$ dotnet new console --output ConsoleApplication
The template "Console App" was created successfully.

Processing post-creation actions...
Running 'dotnet restore' on /home/razin/projects/ConsoleApplication/ConsoleApplication.csproj...
  Determining projects to restore...
  Restored /home/razin/projects/ConsoleApplication/ConsoleApplication.csproj (in 126 ms).
Restore succeeded.

Then build the application using dotnet build:

razin@razin-VirtualBox:~/projects$ cd ConsoleApplication/
razin@razin-VirtualBox:~/projects/ConsoleApplication$ dotnet build
Microsoft (R) Build Engine version 17.0.0-preview-21501-01+bbcce1dff for .NET
Copyright (C) Microsoft Corporation. All rights reserved.

  Determining projects to restore...
  All projects are up-to-date for restore.
  You are using a preview version of .NET. See: https://aka.ms/dotnet-core-preview
  ConsoleApplication -> /home/razin/projects/ConsoleApplication/bin/Debug/net6.0/ConsoleApplication.dll

Build succeeded.
    0 Warning(s)
    0 Error(s)

Time Elapsed 00:00:08.14

Then run the application using dotnet ConsoleApplication.dll:

razin@razin-VirtualBox:~/projects/ConsoleApplication$ dotnet bin/Debug/net6.0/ConsoleApplication.dll
Hello, World!

Great, it works!

How to enable obfuscation?

Now it’s time to add ArmDot packages: ArmDot.Client and ArmDot.Engine.MSBuildTasks. ArmDot.Client contains obfuscation attributes. ArmDot.Engine.MSBuildTasks provides a task that is executed when a project has been built.

Use the following commands: dotnet add package ArmDot.Client and dotnet add package ArmDot.Engine.MSBuildTasks:

razin@razin-VirtualBox:~/projects/ConsoleApplication$ dotnet add package ArmDot.Client
info : PackageReference for package 'ArmDot.Client' version '2021.18.0' added to file '/home/razin/projects/ConsoleApplication/ConsoleApplication.csproj'.
info : Committing restore...
info : Writing assets file to disk. Path: /home/razin/projects/ConsoleApplication/obj/project.assets.json

razin@razin-VirtualBox:~/projects/ConsoleApplication$ dotnet add package ArmDot.Engine.MSBuildTasks
info : PackageReference for package 'ArmDot.Engine.MSBuildTasks' version '2021.18.0' added to file '/home/razin/projects/ConsoleApplication/ConsoleApplication.csproj'.
info : Committing restore...
info : Generating MSBuild file /home/razin/projects/ConsoleApplication/obj/ConsoleApplication.csproj.nuget.g.props.
info : Writing assets file to disk. Path: /home/razin/projects/ConsoleApplication/obj/project.assets.json

We just one step from enabling an obfuscation. Edit the project file, add the new target Protect that is executed after the assembly has been built:

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

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net6.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

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

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

</Project>

Rebuild the project. You will see:

razin@razin-VirtualBox:~/projects/ConsoleApplication$ dotnet build
Microsoft (R) Build Engine version 17.0.0-preview-21501-01+bbcce1dff for .NET
Copyright (C) Microsoft Corporation. All rights reserved.

  Determining projects to restore...
  All projects are up-to-date for restore.
  [ArmDot] ArmDot [Engine Version 2021.18.0.0] (c) Softanics. All Rights Reserved
  [ArmDot] ------ Build started: Assembly (1 of 1): ConsoleApplication.dll (/home/razin/projects/ConsoleApplication/bin/Debug/net6.0/ConsoleApplication.dll) ------
/home/razin/projects/ConsoleApplication/ConsoleApplication.csproj(19,3): warning : [ArmDot] warning ARMDOT0003: No methods to protect in the assembly /home/razin/projects/ConsoleApplication/bin/Debug/net6.0/ConsoleApplication.dll
  [ArmDot] Writing protected assembly to /home/razin/projects/ConsoleApplication/bin/Debug/net6.0/ConsoleApplication.dll...
  [ArmDot] Finished

Build succeeded.

/home/razin/projects/ConsoleApplication/ConsoleApplication.csproj(19,3): warning : [ArmDot] warning ARMDOT0003: No methods to protect in the assembly /home/razin/projects/ConsoleApplication/bin/Debug/net6.0/ConsoleApplication.dll
    2 Warning(s)
    0 Error(s)

Time Elapsed 00:00:09.54

ArmDot produced a warning No methods to protect in the assembly. Indeed, although ArmDot is added to the building process, it does nothing as ArmDot doesn’t know what to obfuscate./p>

To control ArmDot, use ArmDot attributes. In this sample, let’s virtualize all the methods. Edit Program.cs as shown below:

using System.Reflection;

[assembly: ArmDot.Client.VirtualizeCode]

Rebuild the project:

razin@razin-VirtualBox:~/projects/ConsoleApplication$ dotnet build
Microsoft (R) Build Engine version 17.0.0-preview-21501-01+bbcce1dff for .NET
Copyright (C) Microsoft Corporation. All rights reserved.

  Determining projects to restore...
  All projects are up-to-date for restore.
  You are using a preview version of .NET. See: https://aka.ms/dotnet-core-preview
  ConsoleApplication -> /home/razin/projects/ConsoleApplication/bin/Debug/net6.0/ConsoleApplication.dll
  [ArmDot] ArmDot [Engine Version 2021.18.0.0] (c) Softanics. All Rights Reserved
  [ArmDot] ------ Build started: Assembly (1 of 1): ConsoleApplication.dll (/home/razin/projects/ConsoleApplication/bin/Debug/net6.0/ConsoleApplication.dll) ------
  [ArmDot] Conversion started for method System.Void Program::<Main>$(System.String[])
  [ArmDot] Conversion finished for method System.Void Program::<Main>$(System.String[])
  [ArmDot] Conversion started for method System.Void Program::.ctor()
  [ArmDot] Conversion finished for method System.Void Program::.ctor()
  [ArmDot] Writing protected assembly to /home/razin/projects/ConsoleApplication/bin/Debug/net6.0/ConsoleApplication.dll...
  [ArmDot] Finished

Build succeeded.
    1 Warning(s)
    0 Error(s)

Time Elapsed 00:00:08.48

ArmDot showed that it obfuscated several methods. Run the project to ensure it is working well:

razin@razin-VirtualBox:~/projects/ConsoleApplication$ dotnet bin/Debug/net6.0/ConsoleApplication.dll
Hello, World!

Conclusion

.NET has had Linux support for years; compilation of .NET applications became a usual case. That’s why you need an obfuscator that works on Linux. With just a few commands you can add such an obfuscator, ArmDot, to your project. With the help of obfuscation attributes, you control the obfuscator; the ArmDot task allows you to obfuscate a project while building.