Skip to main content
Version: 2024.1

5.3. Module Initializers

5.3.1. Introduction

A module initializer can be seen as a constructor for an assembly (technically it is a constructor for a module; each .NET assembly is comprised of one or more modules, typically just one). It is run when a module is loaded for the first time, and is guaranteed to run before any other code in the module.

Module initializers represent a feature of CLR which is not typically available in conventional .NET languages like C# or VB.NET.

However, module initializers are proven to be indispensable in some situations. For example:

  • To reliably install an assembly loader in a class library that would handle specific scenarios like dynamic handling of probing paths
  • To harden the licensing checks employed by the assembly

That's why Eazfuscator.NET allows to turn a static method of a class into assembly module initializer during obfuscation.

Tip

C# starting with version 9.0 provides out-of-the-box support for module initializers, so you can use just that. Moreover, module initializers provided by a programming language can be used interchangeably or together with the corresponding functionality provided by Eazfuscator.NET.

5.3.2. Instructions

In order to turn a static method into module initializer, a special obfuscation directive should be applied to the method as shown below:

using System;
using System.Reflection;

class Program
{
[Obfuscation(Feature = "module initializer", Exclude = false)]
internal static void ModInit()
{
Console.WriteLine("Module has been initialized.");
}

static void Main(string[] args)
{
Console.WriteLine("App started.");
}
}

What happens under the hood is Eazfuscator.NET creates a new or augments existing module initializer of an assembly by inserting a call to the marked static method. In this way, the given method gets called during module initialization in run time.

5.3.3. Execution Order

The obfuscation directive allows to define several module initializers for an assembly. Like so:

using System;
using System.Reflection;

class Program
{
[Obfuscation(Feature = "module initializer", Exclude = false)]
internal static void ModInit()
{
Console.WriteLine("Module initializer A.");
}

static void Main(string[] args)
{
Console.WriteLine("App started.");
}
}

class Loader
{
[Obfuscation(Feature = "module initializer", Exclude = false)]
internal static void ModInit()
{
Console.WriteLine("Module initializer B.");
}
}

Due to the way .NET metadata are serialized in compiled assembly, the order in which Program.ModInit and Loader.ModInit methods are run is undefined and may differ from one compilation to another.

The good news is a tighter grip on that behavior is available. You can define an explicit order in which module initialization takes place by using a priority syntax for obfuscation directives:

using System;
using System.Reflection;

class Program
{
[Obfuscation(Feature = "1. module initializer", Exclude = false)]
internal static void ModInit()
{
Console.WriteLine("Module initializer A.");
}

static void Main(string[] args)
{
Console.WriteLine("App started.");
}
}

class Loader
{
[Obfuscation(Feature = "2. module initializer", Exclude = false)]
internal static void ModInit()
{
Console.WriteLine("Module initializer B.");
}
}

In that case, the order of module initializers is explicit and always remains the same, so that the given app is guaranteed to always produce the following output after obfuscation:

Module initializer A.  
Module initializer B.
App started.