4.9. Assemblies Embedding
Assemblies embedding allows to embed assembly's dependencies into assembly itself. This may be beneficial from the deployment and security points of view.
Assemblies embedding is similar to merging. The main difference is that the assemblies are not merged into a single assembly when they are embedded. They just get encrypted and packed as the assembly resources. As a result, there is a single assembly at the output, and it contains the packed dependencies at the same file.
4.9.2. Embedding vs. Merging
Assemblies merging delivers the best performance for the resulting assemblies. They can be NGEN'ed; they work in all constrained environments (Windows Phone, Compact Framework, etc.). File mappings and JIT'ted code can be cached by the operating system for such assemblies, bringing the blinding fast application startups. Assembly merging definitely rocks.
However, there are two possible downsides of assembly merging:
- It's not always possible to apply it without breaking the application
- The resulting assembly may become extremely large to the point where it negatively affects the performance
So this is where assemblies embedding comes to the rescue.
Embedded assemblies are easy goals to achieve, and they work out of the box. Downsides? Well, they are present. Embedded assemblies cannot be NGEN'ed; they do not work in some constrained environments (Xbox, Windows Phone and Compact Framework). The extraction of embedded assemblies during the application load is a performance penalty (the penalty is pretty small, so it's unlikely you can notice it).
Assemblies embedding brings some benefits as well. The embedded assemblies are encrypted, so this is a security hardening against the hackers. Embedded assemblies are compressed, bringing the size reduction of the resulting assembly. And of course, assemblies embedding is the easiest way to achieve single-file deployment, making your application to consist of a single .exe (or .dll) file.
To enable assemblies embedding, you should apply specially formed attribute(s) to your assembly. To do that, you can use the instructions below.
Instructions on enabling assemblies embedding
Open obfuscatable project inside the IDE
Add new source file to the project and call it
ObfuscationSettings.cs(for C#) or
ObfuscationSettings.vb(for Visual Basic .NET). You may prefer to use another name instead of
ObfuscationSettings.cswith the following content (C#):
[assembly: Obfuscation(Feature = "embed XXXXXX.dll", Exclude = false)]
For Visual Basic .NET, fill
ObfuscationSettings.vbwith the following content:
<Assembly: Obfuscation(Feature:="embed XXXXXX.dll", Exclude:=False)>Note
Change XXXXXX.dll with the file name of the assembly you want to embed.Important
It is recommended to obfuscate the embedded assemblies.Tip
Eazfuscator.NET automatically finds the assembly path when only the file name is supplied.
If you prefer to specify the exact file path to assembly, then you can use script variables:
[assembly: Obfuscation(Feature = @"embed $(InputDir)\Lib\AssemblyToEmbed.dll", Exclude = false)]Tip
If you want to embed several assemblies, then just add several attributes:
[assembly: Obfuscation(Feature = "embed Assembly1.dll", Exclude = false)]
[assembly: Obfuscation(Feature = "embed AnotherAssembly2.dll", Exclude = false)]
You can use assembly mask syntax to bulk select the assemblies you want to embed:
[assembly: Obfuscation(Feature = "embed Contoso.Dal.*.dll", Exclude = false)]
Embedded assemblies are compressed and encrypted by default. You may prefer to turn off the compression, encryption, or both. To do that, please read the notes below.
The full notation of a custom attribute for assembly embedding has the following form:
[assembly: Obfuscation(Feature = "embed [flags] XXXXXX.dll", Exclude = false)]
[flags] is an optional sequence of flags separated by spaces and enclosed in
 brackets. The available flags are grouped by purpose and described in the tables below.
Table 4.9. The list of general flags for assembly embedding directive
Disables the compression
Disables the encryption
Disables automatic embedding of satellite assemblies
Logs all the assemblies targeted by the directive
Table 4.10. The list of assembly loading flags for assembly embedding directive
Instructs Eazfuscator.NET to load the embedded assembly from a file instead of memory during the obfuscated assembly run-time. This can be used to preserve a meaningful value of the
Instructs Eazfuscator.NET to load the embedded assembly immediately on a module start. This may be required to satisfy the technologies that rely on a custom assembly resolution mechanism. An example of such technology is WPF, which uses
Table 4.11. The list of assembly inclusion flags for assembly embedding directive
Instructs Eazfuscator.NET to automatically decide whether to embed the specified assembly or not. A typical behavior is to always embed the explicitly specified assemblies. The assemblies specified by a mask are only embedded if they are referenced by code. This is the default setting
Always embed the assembly even if it is not referenced by code. This is a useful setting if the assembly specified by a mask is not directly used by the code but instead engaged in a reflection scenario
Embed the assembly only if it is referenced by code
Example 4.28. Embed assembly without compression and encryption
[assembly: Obfuscation(Feature = "embed [no-compress no-encrypt] XXXXXX.dll", Exclude = false)]
Example 4.29. Embed assembly without encryption; compression is enabled
[assembly: Obfuscation(Feature = "embed [no-encrypt] XXXXXX.dll", Exclude = false)]
Example 4.30. Embed assembly; compression and encryption are enabled
[assembly: Obfuscation(Feature = "embed XXXXXX.dll", Exclude = false)]
Example 4.31. Embed own satellite assemblies; compression and encryption are enabled
[assembly: Obfuscation(Feature = "embed satellites", Exclude = false)]
While assembly embedding is the most non-intrusive way to link the assembly, some rare issues may occur. Possible issues are described in this chapter together with corresponding solutions to avoid them.
Possible Issue #1: Location Property of System.Reflection.Assembly Class
Location property of
System.Reflection.Assembly class is often used to find the paths of files near the assembly. While not being a correct solution, this works for most deployment scenarios.
What may go wrong? First of all,
Location property can have a completely unexpected value when assembly shadow copying is used, thus breaking the intended application logic. Secondly,
Location property has a
null value when a corresponding assembly is embedded.
EscapedCodeBase property instead. This property always has a correct value in all deployment scenarios. Please take a look at the sample below.
static string GetEulaPath()
var assembly = typeof(Program).Assembly;
// string location = assembly.Location; // Please do not use this. This is a flawed approach
string location = new Uri(assembly.EscapedCodeBase).LocalPath; // <-- Use this instead
return Path.Combine(Path.GetDirectoryName(location), "EULA.rtf");
An alternative workaround is to use the
load-from-file flag for the embedded assembly.
Possible Issue #2: Custom Assembly Loading
Issue summary. Some applications, libraries, and technologies use custom assembly resolution mechanisms beyond locating assemblies by strong names. They typically rely on some domain-specific properties of an assembly. It may be a special naming convention, specific custom attributes applied to an assembly, etc.
What may go wrong? A particular property of a custom assembly resolution mechanism is that it looks for an assembly either at the list of loaded assemblies or at the probing paths for the current AppDomain. This is where Assemblies Embedding may become a breaking change for the custom algorithm – the assembly is no longer there; it is embedded.
immediate-load flag for the embedded assembly. It instructs Eazfuscator.NET to load the assembly immediately at the module startup, allowing a custom assembly resolution mechanism to take over.