Obfuscation is the language of Gods.

Obfuscar 2.0 errors with loading types from assembly

Reading Time: 5 minutes.

I plugged Obfuscar into my build pipeline (the easiest configuration ever, by the way) because I needed to obfuscate a DLL I was going to push as a NuGet package. The DLL was obfuscated without changing any of the public APIs, Classes or Parameters – only internal stuff was scrambled. Simple stuff.

Essentially, this is what I was working towards:

However, I wasn’t quite having the success I hoped for.

Whilst the obfuscation was successful, and I could even reference the obfuscated DLL and use it in my other project just fine, when importing the DLL to PowerShell our whole team would run into different flavors of this error:

“Could not load type from assembly”

Or

“Cannot find type [typename]: Verify that the assembly containing this type is loaded.”

In PowerShell, running this:

$assembly = [Reflection.Assembly]::LoadFile([pathtodll])
$myObj = New-Object -TypeName "[MyNamespace].[PublicType]"

Resulted in this:

PowerShell throwing error "Cannot find type [typename]: Verify that the assembly containing this type is loaded." when we're trying to load our obfuscated assembly.
PowerShell throwing error “Cannot find type [typename]: Verify that the assembly containing this type is loaded.” when we’re trying to load our obfuscated assembly.

Even though the same type was instantiable just fine when referencing the DLL from another project! So what’s wrong with PowerShell, then??

Reason

Well, it turns out PowerShell just gets confused by obfuscated DLLs and there’s a fair chance of an OutOfMemoryException being thrown internally, then swallowed, and a more general “whoops we failed to perform whatever you requested” to be returned. This time being loading types, because that’s required to instantiate a new object.

A discussion about this can be found here: https://github.com/obfuscar/obfuscar/issues/171

Solution

Luckily, this can be solved with the following configuration (most of the time):

Open obfuscar.xml (or whatever file you use to configure Obfuscar in your project) and make sure you have these switches there:

How to configure Obfuscar to ignore Public API and some stringvalues when obfuscating your assembiles:

  
  
  

  
  
  
  

  
  

HideStrings being false in combination of the types you’re using being non-obfuscated is the key here. In my case, I was happy just not obfuscating any of the Public members and methods, but you can also add this parameter to the ones you want to keep and set KeepPublicApi as false:

You can also add exclusion parameters to obfuscar.xml file, to skip obfuscating namespaces, types, methods or properties – see below for an example (source):

How to configure Obfuscar to ignore certain namespaces, types, methods or properties:

  
  

  
  

  
  

  
  

  
  

  
  
  
  
  
  

  
  
  
  
  
  
  
  
  
  
  
  

  
  
  
  

  
  
  
  

  
  

Alternatively, you can just use the ObfuscationAttribute to exclude certain items from the obfuscation – a lengthy, thorough example below:

How to configure any decent obfuscator to ignore certain particular namespaces, types, methods or properties:
using System;
using System.Reflection;

// Mark this public assembly for obfuscation.
[assembly:ObfuscateAssemblyAttribute(false)]

// This class is marked for obfuscation, because the assembly
// is marked.
public class Type1
{

    // Exclude this method from obfuscation. The default value
    // of the Exclude property is true, so it is not necessary
    // to specify Exclude=True, although spelling it out makes
    // your intent clearer.
    [ObfuscationAttribute(Exclude=true)]
    public void MethodA() {}

    // This member is marked for obfuscation because the class
    // is marked.
    public void MethodB() {}
}

// Exclude this type from obfuscation, but do not apply that
// exclusion to members. The default value of the Exclude 
// property is true, so it is not necessary to specify 
// Exclude=true, although spelling it out makes your intent 
// clearer.
[ObfuscationAttribute(Exclude=true, ApplyToMembers=false)]
public class Type2
{

    // The exclusion of the type is not applied to its members,
    // however in order to mark the member with the "default" 
    // feature it is necessary to specify Exclude=false,
    // because the default value of Exclude is true. The tool
    // should not strip this attribute after obfuscation.
    [ObfuscationAttribute(Exclude=false, Feature="default", 
        StripAfterObfuscation=false)]
    public void MethodA() {}

    // This member is marked for obfuscation, because the 
    // exclusion of the type is not applied to its members.
    public void MethodB() {}

}

// This class only exists to provide an entry point for the
// assembly and to display the attribute values.
internal class Test
{

    public static void Main()
    {

        // Display the ObfuscateAssemblyAttribute properties
        // for the assembly.        
        Assembly assem = typeof(Test).Assembly;
        object[] assemAttrs = assem.GetCustomAttributes(
            typeof(ObfuscateAssemblyAttribute), false);

        foreach( Attribute a in assemAttrs )
        {
            ShowObfuscateAssembly((ObfuscateAssemblyAttribute) a);
        }

        // Display the ObfuscationAttribute properties for each
        // type that is visible to users of the assembly.
        foreach( Type t in assem.GetTypes() )
        {
            if (t.IsVisible)
            {
                object[] tAttrs = t.GetCustomAttributes(
                    typeof(ObfuscationAttribute), false);

                foreach( Attribute a in tAttrs )
                {
                    ShowObfuscation(((ObfuscationAttribute) a),
                        t.Name);
                }

                // Display the ObfuscationAttribute properties
                // for each member in the type.
                foreach( MemberInfo m in t.GetMembers() )
                {
                    object[] mAttrs = m.GetCustomAttributes(
                        typeof(ObfuscationAttribute), false);

                    foreach( Attribute a in mAttrs )
                    {
                        ShowObfuscation(((ObfuscationAttribute) a), 
                            t.Name + "." + m.Name);
                    }
                }
            }
        }
    }

    private static void ShowObfuscateAssembly(
        ObfuscateAssemblyAttribute ob)
    {
        Console.WriteLine("\r\nObfuscateAssemblyAttribute properties:");
        Console.WriteLine("   AssemblyIsPrivate: {0}", 
            ob.AssemblyIsPrivate);
        Console.WriteLine("   StripAfterObfuscation: {0}",
            ob.StripAfterObfuscation);
    }

    private static void ShowObfuscation(
        ObfuscationAttribute ob, string target)
    {
        Console.WriteLine("\r\nObfuscationAttribute properties for: {0}",
            target);
        Console.WriteLine("   Exclude: {0}", ob.Exclude);
        Console.WriteLine("   Feature: {0}", ob.Feature);
        Console.WriteLine("   StripAfterObfuscation: {0}",
            ob.StripAfterObfuscation);
        Console.WriteLine("   ApplyToMembers: {0}", ob.ApplyToMembers);
    }
}

/* This code example produces the following output:

ObfuscateAssemblyAttribute properties:
   AssemblyIsPrivate: False
   StripAfterObfuscation: True

ObfuscationAttribute properties for: Type1.MethodA
   Exclude: True
   Feature: all
   StripAfterObfuscation: True
   ApplyToMembers: True

ObfuscationAttribute properties for: Type2
   Exclude: True
   Feature: all
   StripAfterObfuscation: True
   ApplyToMembers: False

ObfuscationAttribute properties for: Type2.MethodA
   Exclude: False
   Feature: default
   StripAfterObfuscation: False
   ApplyToMembers: True
 */

That’s it – with the configuration to exclude whatever confuses your PowerShell in place, you should be good!

mm
0 0 vote
Article Rating
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments