Protecting .NET Core's .exe files
Published 30 March 2020
.NET Core introduced some features that produce partially-native or fully-native assemblies. These cannot be protected directly, because SmartAssembly only supports assemblies containing managed .NET code.
Depending on the feature, there are ways to protect those assemblies despite SmartAssembly's limitations.
Scenario 1: Default executables
Always choose a .dll input file when protecting .NET Core applications.
Fundamentally, every .NET Core assembly is a .dll file. Console applications, class libraries, Windows desktop applications (WPF or WinForms, introduced in .NET Core 3) — they're all .dll files, no matter if they're executables or libraries.
This is contrary to .NET Framework where executables (console applications and desktop applications) are .exe files, and class libraries are .dll files.
In .NET Core, the .dll file of console and desktop applications differs from the .dll file of class libraries in that it contains an entry point. In simplest terms, an entry point is a method to be executed when running the assembly.
Executing a .NET Core application
To execute a .NET Core application contained in a .dll file you need what's called an appHost or .NET Core CLI.
Using a .NET Core CLI consists of running the following command:
dotnet.exe path/to/MyAssembly.dll
An appHost on the other hand, is an .exe file located next to your assembly (in the output directory) with the same file name as your application. If your application is MyAssembly.dll, then the appHost will be named MyAssembly.exe.
Despite a similar name, it's important to remember that the appHost is just a native executable assembly used to run your application. Think of it as dotnet.exe renamed to MyAssembly.exe. When executed (i.e. by double-clicking) it locates your application's assembly (MyAssembly.dll) and its configuration files, and then executes it by calling the entry point.
Most of the times, when MyAssembly.exe and MyAssembly.dll both exist in .NET Core application's directory, all of your application's code is located in the MyAssembly.dll file.
MyAssembly.exe is a native assembly (appHost) and it's only used to execute the managed code from MyAssembly.dll. The appHost executable doesn't contain any sensitive information and doesn't need to be protected.
Since .NET Core 3.0, the .exe file (appHost) is created for every build. For .NET Core 2.x it would only be created when publishing the application.
Changes in SmartAssembly
Starting with SmartAssembly 7.4, after choosing a native .exe file when the .dll file with the same name exists in the same directory, you will be asked to load the .dll file instead. Clicking on the name of the .dll file will automatically load it:
Scenario 2: Single-file executables
.NET Core is known to produce large number of files. This is especially noticeable when publishing the application as self-contained, which copies hundreds of assemblies into the output directory.
Since .NET Core 3.0 it's possible to bundle the main assembly, all of its dependencies, configuration files, and core framework assemblies into one executable. When executing the bundled assembly for the first time, the files are unpacked into temporary directory to allow for normal program execution.
The bundle (also known as single-file executable) is a native assembly and cannot be directly processed by SmartAssembly.
The only way of protecting your main assembly and its dependencies is to integrate SmartAssembly using an MSBuild task. This allows you to protect your assembly and its dependencies by executing SmartAssembly before .NET Core build process bundles it into single-file executable.
To learn more, see: Using SmartAssembly with single-file executables (.NET Core 3).
Scenario 3: ReadyToRun images
.NET Core 3.0 introduced the ability to produce "ready-to-run" assemblies. The resulting assembly is primarily a managed assembly, with pre-compiled native code added in order to improve the startup performance by limiting the amount of work required by the JIT.
Processing such assembly directly in SmartAssembly is not possible, as it doesn't support processing mixed-mode assemblies (containing native code).
However, similarly to previous scenario, it's possible to protect your assembly before it is pre-compiled into ready-to-run assembly by using SmartAssembly's MSBuild task.
To learn more, see: Using SmartAssembly with ReadyToRun images (.NET Core 3).