C Sharp Reflection hack to determine implicit default constructor vs. declared constructor

Posted in software by Christopher R. Wirz on Mon Jul 24 2017



If you followed the recent hack on the cryptocurrency Ethereum, then you know the advantage of marking methods as internal or protected. In many cases, however, developers create public classes, but do not specify a constructor if no initialization is necessary. While it may keep the code clean, this allows for a potential vulnerability as there will be an implicit constructor.

Using reflection, it is possible to determine if a given type has a specified constructor or an implicit constructor. If a constructor has been defined - whether it takes any parameters or not, an implicit constructor will not be defined. The following code shows how to detect an implicit constructor.

Note: This approach has been validated across 5,000+ test cases, but that does not mean it will always work. It just works really really often.

As it turns out, an explicit constructor will have one more byte than an implicit constructor of the same signature. An implicit parameterless constructor with no initialization has 8 bytes and an explicit constructor with no initialization has 9 bytes. Since all properties are backed by fields, the declared field count is a driver of the constructor's Intermediate Language (IL) body byte length. Each declared field adds 7 bytes to the constructor's IL body byte length.


public static partial class TypeExtensions
{
	/// <summary>
	/// 	Checks if a default constructor has probably been generated.
	/// </summary>
	/// <param name="t">The type object.</param>
	/// <returns>true if the type has an implicit default constructor</returns>
	public static bool HasImplicitDefaultConstructor(this Type t)
	{
		if (
			t.IsClass && // Classes are given implicit constructors
			!t.IsAbstract && // But abstract classes can not have implicit constructors
			!t.IsEnum // Enums do not need constructors
		) 
		{
			if (
				// The type has a public constructor
				t.GetConstructors(BindingFlags.Instance | BindingFlags.Public).Any() 
				// and
				&&
				// If any other constructor is specified,
				t.GetConstructors(BindingFlags.Instance | BindingFlags.Public)
					.All(
						c =>
						// The body is as long as 8 + 7*(the number of fields that have values)
						c.GetMethodBody()?.GetILAsByteArray().Length > 7 &&
						(c.GetMethodBody()?.GetILAsByteArray().Length - 8) % 7 == 0
						// and
						&& 
						// Therea are no parameters or all are optional
						(c.GetParameters().Length == 0 || c.GetParameters().All(p => p.IsOptional))
					)
				)
			{
				return true;
			}
		}
		return false;
	}
}

We can now experiment with the following classes:


internal class Test1
{
	int i = 0;
	Test1()
	{
	}
}

public class Test2
{
}

public class Test3
{

	public Test3()
	{
	}
}

public class Test21
{
	int i = 2;
}

public class Test31
{
	int i = 2;

	public Test31()
	{
	}
}

internal class Test22
{
	int i = 2;
	int n { get; set; } = 3;
}

internal class Test32
{
	int i = 2;
	int n { get; set; } = 3;

	public Test32()
	{
	}
}
internal class Test4
{
	int i = 2;
	int n = 3;
	protected Test4()
	{
	}
}

internal class Test5
{
	int i = 2;
	int n { get; set; } = 3;

	public Test5()
	{
		i = 3;
		n = 4;
	}
}

And run the following code:


if (!typeof(Test1).HasImplicitDefaultConstructor())
{
	Console.WriteLine("Test1 has explicit constructor");
}
if (typeof(Test2).HasImplicitDefaultConstructor())
{
	Console.WriteLine("Test2 has implicit constructor");
}
if (!typeof(Test3).HasImplicitDefaultConstructor())
{
	Console.WriteLine("Test3 has explicit constructor");
}
if (typeof(Test21).HasImplicitDefaultConstructor())
{
	Console.WriteLine("Test21 has implicit constructor");
}
if (!typeof(Test31).HasImplicitDefaultConstructor())
{
	Console.WriteLine("Test31 has explicit constructor");
}
if (typeof(Test22).HasImplicitDefaultConstructor())
{
	Console.WriteLine("Test22 has implicit constructor");
}
if (!typeof(Test32).HasImplicitDefaultConstructor())
{
	Console.WriteLine("Test32 has explicit constructor");
}
if (!typeof(Test4).HasImplicitDefaultConstructor())
{
	Console.WriteLine("Test4 has explicit constructor");
}
if (!typeof(Test5).HasImplicitDefaultConstructor())
{
	Console.WriteLine("Test5 has explicit constructor");
}
Console.ReadKey();

The results are as follows:

Test1 has explicit constructor
Test2 has implicit constructor
Test3 has explicit constructor
Test21 has implicit constructor
Test31 has explicit constructor
Test22 has implicit constructor
Test32 has explicit constructor
Test4 has explicit constructor
Test5 has explicit constructor