Protecting C# Applications That Use Dynamic Types
Published on December 4, 2019 by John Brawner
The dynamic type in C# provides flexibility that is not available in other statically-typed languages. Since its introduction in C# 4.0 (.NET 4.5), we have worked with customers who wanted to know more about how dynamic types are impacted by the obfuscation process.
When a dynamic type is used, the compiler generates reflection calls to its member methods (and/or properties). These reflection calls use a string representation of the method name to invoke that method at runtime. Post-obfuscation, this string must match the method name to avoid breaking runtime behavior.
In most cases, applications that use dynamic types will work fine after Dotfuscator protects them. This is because the dynamic type usually resolves to a class defined in an assembly not included in Dotfuscator’s inputs (and therefore not subject to Renaming). This includes types from third-party assemblies, types from unmanaged assemblies, and also built-in types – int, bool, string. On the other hand, if the dynamic type resolves to a type defined in one of Dotfuscator’s input assemblies, we need to set a Dotfuscator rename exclusion to preserve those compiler-generated reflection calls.
Please consider the following example:
The dynamic type resolves to MyClass, as defined in our code. It then calls MyMethod from MyClass. After compiling this code, notice the compiler loads a string representation of MyMethod onto the stack to be loaded via reflection. From the MSIL:
If this code is obfuscated without rename exclusions, we’d experience a runtime error:
Unhandled Exception: Microsoft.CSharp.RuntimeBinder.RuntimeBinderException:
'a.a' does not contain a definition for 'MyMethod'
at CallSite.Target(Closure , CallSite , Object , String )
at System.Dynamic.UpdateDelegates.UpdateAndExecuteVoid2[T0,T1](CallSite site, T0 arg0, T1 arg1)
at a.a() in C:\SampleCode\Dynamic\Dynamic\Program.cs:line 25
In order to avoid this runtime error, I’ve excluded “MyMethod” from Renaming.
The same idea would have applied if we were loading a property from MyClass.
Elsewhere in the example, there is a dynamic type that resolves to a built-in type. This code runs properly without additional Dotfuscator configuration, even though I switch between int and string at runtime:
If you use and benefit from the flexibility that dynamic types provide, these guidelines will help you get the most protection, while still preserving correct runtime behavior.
The full example can be downloaded here.
If you have any feedback on this topic, or other topics you would like us to discuss in the Support Corner, please feel free to contact us at email@example.com.