Reputation: 5961
This question is very similar to a two other questions see: first, second. However those are pretty out dated to say the least and I hope things have changed with .Net 5.
Now first up let me clarify the issue. With a simple example that tries to get the underlying array of a List<int>.
var method = new DynamicMethod("GetUnderlyingArray", typeof(int[]), new[] { typeof(List<int>) }, typeof(List<int>), true);
var ilGenerator = method.GetILGenerator();
ilGenerator.Emit(OpCodes.Ldarg_0);
ilGenerator.Emit(OpCodes.Ldfld, typeof(List<int>).GetField("_items", BindingFlags.NonPublic | BindingFlags.Instance));
ilGenerator.Emit(OpCodes.Ret);
var arrayGetter = (Func<List<int>, int[]>)method.CreateDelegate(typeof(Func<List<int>, int[]>));
This works perfectly fine as I am able to tell the DynamicMethod
to skip the visibility checks (even though it works as well, when the last parameter of the DynamicMethod
constructor true
is removed).
However, when I am trying to do the same with the example down below, it will throw a FieldAccessException
.
var assemblyName = new AssemblyName("Example");
var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
var dynamicModule = assemblyBuilder.DefineDynamicModule(assemblyName.Name + ".dll");
var type = dynamicModule.DefineType("GetUnderlyingArrayClass");
var method = type.DefineMethod("GetUnderlyingArray", MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Static, typeof(int[]), new[] { typeof(List<int>) });
var ilGenerator = method.GetILGenerator();
ilGenerator.Emit(OpCodes.Ldarg_0);
ilGenerator.Emit(OpCodes.Ldfld, typeof(List<int>).GetField("_items", BindingFlags.NonPublic | BindingFlags.Instance));
ilGenerator.Emit(OpCodes.Ret);
type.CreateType();
var arrayGetter = (Func<List<int>, int[]>)type.GetMethod("GetUnderlyingArray").CreateDelegate(typeof(Func<List<int>, int[]>));
System.FieldAccessException: Attempt by method 'GetUnderlyingArrayClass.GetUnderlyingArray(System.Collections.Generic.List'1)' to access field 'System.Collections.Generic.List'1<System.Int32>._items' failed. + GetUnderlyingArrayClass.GetUnderlyingArray(List)
Here is a .Net fiddle link with the code shown above.
Now, one of the questions I mentioned points to the following attribute ReflectionPermissionAttribute
. However as it states on the documentation Code Access Security is not supported or honored by the runtime.
. From what I understand, this basically means that .Net Core/.Net 5 do not support CAS.
Here is where I am getting confused. Setting the skipVisibility
parameter to true
or false
doesn't actually matter. I'd assume that is due to the fact that I am running the code in a .Net 5 environment. However, if CAS is not supported on .Net 5, why am I still able to read out the private field?
The goal is obviously to access a private field/method from a dynamically generated method using the DefineType
/DefineMethod
API's.
Upvotes: 2
Views: 492
Reputation: 5961
I am still not able to explain why setting the skipVisibility
parameter to true
/false
doesn't matter, however PathogenDavid over on the GitHub from the csharp runtime discussion repository showed me a solution which was way easier than expected.
// Add IgnoresAccessChecksTo attribute
var ignoresAccessChecksTo = new CustomAttributeBuilder
(
typeof(IgnoresAccessChecksToAttribute).GetConstructor(new Type[] { typeof(string) }),
new object[] { typeof(List<>).Assembly.GetName().Name }
);
assemblyBuilder.SetCustomAttribute(ignoresAccessChecksTo);
See this .Net Fiddle for a working example.
As he said the IgnoresAccessChecksTo
attribute is used to bypass the checks.
The runtime understands the attribute, but it is not defined in the BCL so you have to define it yourself.
Upvotes: 2