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 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.
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.
The only downside of merging is that it's not always possible to apply it without breaking the application. So this is the point 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 can not be NGEN'ed, they do not work in some constrained environments (Xbox, Windows Phone and Compact Framefork). The extraction of embedded assemblies during the application load is a performance penalty (penalty is pretty small, so it's unlikely you are able to notice it).
Assemblies embedding brings some benefits as well. The embedded assemblies are encrypted, so this is a securty 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.
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#) orObfuscationSettings.vb
(for Visual Basic .NET). You may prefer to use another name instead ofObfuscationSettings.cs
orObfuscationSettings.vb
Fill
ObfuscationSettings.cs
with the following content (C#):using System; using System.Reflection; [assembly: Obfuscation(Feature = "embed XXXXXX.dll", Exclude = false)]
For Visual Basic .NET, fill
ObfuscationSettings.vb
with the following content:Imports System Imports System.Reflection <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 obsuscate 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)] …
Embedded assemblies are compressed and encrypted by default. You may prefer to turn off the compression, encryption or them both. In order 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)]
where [flags]
is an optional enumeration of flags separated by spaces.
The list of available flags is presented in the table below.
Table 4.7. The list of flags for assembly embedding attribute
Flag | Description |
---|---|
no_compress | Disables the compression |
no_encrypt | Disables the encryption |
no_satellites | Disables automatic embedding of satellite assemblies |
load_from_file |
Instructs Eazfuscator.NET to load the embedded assembly from file instead of memory during the obfuscated assembly run-time.
This can be used to preserve a meaningful value of Location property from System.Reflection.Assembly type.
|
immediate_load |
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 XmlnsDefinitionAttribute to locate the assemblies at runtime.
Please note that Eazfuscator.NET automatically detects the most of affected technologies and applies the flag automatically when required.
|
Let's take a look on examples.
Example 4.28. Embed assembly without compression and encryption
using System; using System.Reflection; [assembly: Obfuscation(Feature = "embed [no_compress no_encrypt] XXXXXX.dll", Exclude = false)]
Example 4.29. Embed assembly without encryption; compression is enabled
using System; using System.Reflection; [assembly: Obfuscation(Feature = "embed [no_encrypt] XXXXXX.dll", Exclude = false)]
Example 4.30. Embed assembly; compression and encryption are enabled
using System; using System.Reflection; [assembly: Obfuscation(Feature = "embed XXXXXX.dll", Exclude = false)]
Example 4.31. Embed own satellite assemblies; compression and encryption are enabled
using System; using System.Reflection; [assembly: Obfuscation(Feature = "embed satellites", Exclude = false)]
Issue summary.
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.
Solution.
Use EscapedCodeBase
property instead. This property always has a correct value in all deployment scenarios. Please take a look at the sample below.
using System; class Program { 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"); } }
Alternative workaround is to use load_from_file
flag for the embedded assembly.
Issue summary. Some applications, libraries and technologies use custom assembly loading mechanisms that go beyond locating assemblies by strong names. They typically rely on some domain-specific properties of an assembly. It may be special assembly name convention, specific custom attribute applied to an assembly etc.
What may go wrong? A particular property of a custom assembly loading 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.
Solution.
Use immediate_load
flag for the embedded assembly.
This will instruct Eazfuscator.NET to immediately load the assembly at the module startup.