5.4. Private Protected Visibility
.NET languages offer a few keywords for visibility control between assemblies, classes and members. For example, C# has public
, internal
, protected
, protected internal
and private
access modifiers.
Sometimes you may need a special private protected
accessibility level. It exists in Managed C++ but absent in older VB.NET and C# versions. It corresponds to FamANDAssem
visibility scope in terms of .NET CLR.
C# starting with version 7.2 provides private protected
access modifier. Visual Basic starting with version 15.5 provides Private Protected
access modifier. This is a preferred way to go, instead of relying on Eazfuscator.NET to achieve the very same functionality.
Availability of private protected
accessibility level is pretty rare requirement, so let's take a look on specific example. Suppose you have a DLL assembly written in older C# version (before 7.2) that does XML serialization for some entities:
Example 5.5. Original code
using System;
using System.Xml.Serialization;
// This class is used by System.Xml.Serialization.XmlSerializer.
public class Card
{
public string ID
{
get;
set;
}
protected virtual void Validate()
{
}
}
// This class is used by System.Xml.Serialization.XmlSerializer.
public class VerticalCard : Card
{
public int Height
{
get;
set;
}
protected override void Validate()
{
if (Height <= 0)
throw new Exception("Vertical card height should be a positive number greater than zero.");
}
}
See that Validate
method? It won't be renamed after obfuscation despite the wish of a developer. But why? The answer is: because assembly is DLL, the class is public and Validate
method is protected. This means that Validate
method can be reached by a third-party assembly and Eazfuscator.NET leaves its name intact.
That's ok for most situations.
Still, let's imagine that one picky developer decides to rename Validate
method whatever it costs. Potential workarounds are:
-
Make
Validate
methods private. Won't work because it wouldn't be possible to overrideValidate
method inVerticalCard
class -
Make
Card
andVerticalCard
classes internal. Won't work becauseXmlSerializer
works on public classes only -
Make
Validate
methods internal. Will work but will break the visibility borders inside the assembly. This may be unfeasible if you work in a team with established responsibility borders between its members -
Make
Validate
methodsprivate protected
. Will perfectly work, but only in Managed C++. Older VB.NET and C# versions have no corresponding access modifier -
Move validation away to a separate set of classes. Will work, but it requires code refactoring. It may be risky for a large code base and thus not always suitable
So we are stuck if our code is in C# or VB.NET.
Fortunately Eazfuscator.NET can change the visibility of class members to FamANDAssem
level. This is the exact same thing as protected private
in Managed C++. Having that, we can now solve the dilemma (C#):
Example 5.6. Modified code to allow family and assembly visibility for specified methods
using System;
using System.Xml.Serialization;
using System.Reflection;
// This class is used by System.Xml.Serialization.XmlSerializer.
public class Card
{
public string ID
{
get;
set;
}
[Obfuscation(Feature = "family and assembly visibility", Exclude = false)]
protected virtual void Validate()
{
}
}
// This class is used by System.Xml.Serialization.XmlSerializer.
public class VerticalCard : Card
{
public int Height
{
get;
set;
}
[Obfuscation(Feature = "family and assembly visibility", Exclude = false)]
protected override void Validate()
{
if (Height <= 0)
throw new Exception("Vertical card height should be a positive number greater than zero.");
}
}
Once that in place, Validate
methods will be renamed and will no longer be visible to other assemblies.
Instructions on changing visibility to FamANDAssem level for a class member
-
Open the source code of a class member that should have a visibility change
-
Add a custom attribute as shown below (C#):
using System;
using System.Reflection;
class YourClass
{
[Obfuscation(Feature = "family and assembly visibility", Exclude = false)]
protected void YourMethod()
{
...
}
}For Visual Basic .NET:
Imports System
Imports System.Reflection
Class YourClass
<Obfuscation(Feature:="family and assembly visibility", Exclude:=False)>
Protected Sub YourMethod()
...
End Sub
End Class
If you change visibility of a virtual method then it is beneficial to ensure that the whole inheritance hierarchy has the corresponding change. This will improve renaming coverage during obfuscation.
It may be a good idea to turn on code verification for your assembly when visibility changes are applied to ensure that the generated code conforms to the industrial standard of quality.