[C#] 解决Silverlight反射安全关键(SecuritySafeCritical)时报“System.MethodAccessException: 安全透明方法 XXX 无法使用反射访问”的问题
作者: zyl910
一、缘由
在Silverlight中使用反射动态访问时,经常遇到“System.MethodAccessException: 安全透明方法 XXX 无法使用反射访问……”等错误。
其中最常见的情况,是因为这些成员具有 安全关键(SecuritySafeCritical)的特性(SecuritySafeCriticalAttribute)。但是这个现象是不对劲的——Silverlight里编写的代码都是透明代码(SecurityTransparent),按照规则不能调用 关键代码(SecurityCritical),但规则允许它调用安全关键代码(SecuritySafeCritical)。
而且后来测试了硬编码来调用,发现此时(硬编码)是能够正常调用的。且在.NET framework等平台中,是能正常调用的。
虽然硬编码能调用,但是很多时候是需要反射动态访问的。故需要想办法解决。
二、以Assembly.FullName为例进行尝试
2.1 硬编码
例如 Assembly 的 FullName属性就行这种情况。在硬编码的情况下是能成功调用的,源码如下。
Assembly assembly = typeof(Environment).Assembly;
Debug.WriteLine(string.Format("#FullName:\t{0}", assembly.FullName));
2.2 用Type.InvokeMember进行反射调用
可是当使用反射来访问该属性时,便会遇到异常了。例如通过 Type.InvokeMember 来反射获取FullName属性值,源码如下。
Assembly assembly = typeof(Environment).Assembly;
String membername = "FullName";
Object obj = assembly;
Type typ = obj.GetType();
Object rt = typ.InvokeMember(membername, BindingFlags.GetProperty, null, obj, null);
Debug.WriteLine(rt);
详细的异常内容是——
System.MethodAccessException: 安全透明方法 System.Reflection.Assembly.get_FullName() 无法使用反射访问 LibShared.LibSharedUtil.GetPropertyValue(System.Type, System.Object, System.String, Boolean ByRef)。
位于 System.RuntimeMethodHandle.PerformSecurityCheck(Object obj, RuntimeMethodHandleInternal method, RuntimeType parent, UInt32 invocationFlags)
位于 System.RuntimeMethodHandle.PerformSecurityCheck(Object obj, IRuntimeMethodInfo method, RuntimeType parent, UInt32 invocationFlags)
位于 System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean skipVisibilityChecks)
位于 System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
位于 System.RuntimeType.InvokeMember(String name, BindingFlags bindingFlags, Binder binder, Object target, Object[] providedArgs, ParameterModifier[] modifiers, CultureInfo culture, String[] namedParams)
位于 System.Type.InvokeMember(String name, BindingFlags invokeAttr, Binder binder, Object target, Object[] args)
位于 LibShared.LibSharedUtil.GetPropertyValue(Type typ, Object obj, String membername, Boolean & ishad)
可见是 RuntimeMethodHandle.PerformSecurityCheck 在做安全检查时,抛出异常的。
2.3 用 PropertyInfo.GetValue 进行反射调用
既然Type.InvokeMember,那就再试试用 PropertyInfo.GetValue 进行反射调用吧。源码如下。
Assembly assembly = typeof(Environment).Assembly;
String membername = "FullName";
Object obj = assembly;
Type typ = obj.GetType();
PropertyInfo pi = typ.GetProperty(membername);
rt = pi.GetValue(obj, null);
Debug.WriteLine(rt);
发现该办法也是报异常。详细的异常内容是——
System.MethodAccessException: 安全透明方法 System.Reflection.RuntimeAssembly.get_FullName() 无法使用反射访问 LibShared.LibSharedUtil.GetPropertyValue(System.Type, System.Object, System.String, Boolean ByRef)。
位于 System.RuntimeMethodHandle.PerformSecurityCheck(Object obj, RuntimeMethodHandleInternal method, RuntimeType parent, UInt32 invocationFlags)
位于 System.RuntimeMethodHandle.PerformSecurityCheck(Object obj, IRuntimeMethodInfo method, RuntimeType parent, UInt32 invocationFlags)
位于 System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean skipVisibilityChecks)
位于 System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
位于 System.Reflection.RuntimePropertyInfo.GetValue(Object obj, BindingFlags invokeAttr, Binder binder, Object[] index, CultureInfo culture)
位于 System.Reflection.RuntimePropertyInfo.GetValue(Object obj, Object[] index)
位于 LibShared.LibSharedUtil.GetPropertyValue(Type typ, Object obj, String membername, Boolean & ishad)
它也是 RuntimeMethodHandle.PerformSecurityCheck 在做安全检查时,抛出异常的。
还发现 System.Reflection.Assembly.get_FullName 实际是派生类(RuntimeAssembly)里覆写的方法。
2.4 用 MethodInfo.Invoke 进行反射调用
最后还可尝试用 MethodInfo.Invoke 进行反射调用吧。源码如下。
Assembly assembly = typeof(Environment).Assembly;
String membername = "FullName";
Object obj = assembly;
Type typ = obj.GetType();
PropertyInfo pi = typ.GetProperty(membername);
MethodInfo mi = pi.GetGetMethod();
rt = mi.Invoke(obj, null);
Debug.WriteLine(rt);
发现该办法仍是报异常。详细的异常内容是——
System.MethodAccessException: 安全透明方法 System.Reflection.Assembly.get_FullName() 无法使用反射访问 LibShared.LibSharedUtil.GetPropertyValue(System.Type, System.Object, System.String, Boolean ByRef)。
位于 System.RuntimeMethodHandle.PerformSecurityCheck(Object obj, RuntimeMethodHandleInternal method, RuntimeType parent, UInt32 invocationFlags)
位于 System.RuntimeMethodHandle.PerformSecurityCheck(Object obj, IRuntimeMethodInfo method, RuntimeType parent, UInt32 invocationFlags)
位于 System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean skipVisibilityChecks)
位于 System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
位于 System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters)
位于 LibShared.LibSharedUtil.GetPropertyValue(Type typ, Object obj, String membername, Boolean& ishad)
它也是 RuntimeMethodHandle.PerformSecurityCheck 在做安全检查时,抛出异常的。
三、反编译的分析
这种不能反射是很奇怪的,于是决定反汇编看看代码。
这些类型是在mscorlib.dll里定义的。可用ILSpy等工具进行反编译分析。
// C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\Silverlight\v5.0\mscorlib.dll
// mscorlib, Version=5.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e
3.1 Assembly.FullName
Assembly.FullName 反编译后的代码为 ——
// System.Reflection.Assembly
public virtual string FullName
{
get
{
throw new NotImplementedException();
}
}
发现一个疑点——它没有安全关键(SecuritySafeCritical)的特性,那么它应该是透明(SecurityTransparent)的啊。怎么会报MethodAccessException异常呢?
3.2 RuntimeAssembly.FullName
RuntimeAssembly.FullName 反编译后的代码为 ——
// System.Reflection.RuntimeAssembly
public override string FullName
{
[SecuritySafeCritical]
get
{
if (this.m_fullname == null)
{
string value = null;
RuntimeAssembly.GetFullName(this.GetNativeHandle(), JitHelpers.GetStringHandleOnStack(ref value));
Interlocked.CompareExchange<string>(ref this.m_fullname, value, null);
}
return this.m_fullname;
}
}
原来安全关键(SecuritySafeCritical)的特性是在这里出现的。
3.3 System.RuntimeMethodHandle
System.RuntimeMethodHandle 反编译后的代码为 ——
// System.RuntimeMethodHandle
[SecurityCritical]
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void PerformSecurityCheck(object obj, RuntimeMethodHandleInternal method, RuntimeType parent, uint invocationFlags);
[SecurityCritical]
internal static void PerformSecurityCheck(object obj, IRuntimeMethodInfo method, RuntimeType parent, uint invocationFlags)
{
RuntimeMethodHandle.PerformSecurityCheck(obj, method.Value, parent, invocationFlags);
GC.KeepAlive(method);
}
它调了CLR中的内部代码(MethodImplOptions.InternalCall
),无法反编译。不能再跟踪下去了。
四、动态生成代码
所有的反射用法都试过了,均无法成功访问这些属性。此时没路了吗?
不,此时还可尝试动态生成代码的办法。因为之前硬编码时能调通。
C#有2种动态生成代码的办法,分别是 IL Emit 与 Expression Tree。考虑到代码的可读性与可维护性,一般用Expression Tree比较好。
4.1 初试用Expression Tree 动态访问属性
以下是一个辅助函数,利用Expression Tree技术,将属性访问操作封装为一个委托。
/// <summary>
/// 根据 PropertyInfo , 创建 Func 委托.
/// </summary>
/// <param name="pi">属性信息.</param>
/// <returns>返回所创建的 Func 委托.</returns>
public static Func<object, object> CreateGetFunction(PropertyInfo pi) {
MethodInfo getMethod = pi.GetGetMethod();
ParameterExpression target = Expression.Parameter(typeof(object), "target");
UnaryExpression castedTarget = getMethod.IsStatic ? null : Expression.Convert(target, pi.DeclaringType);
MemberExpression getProperty = Expression.Property(castedTarget, pi);
UnaryExpression castPropertyValue = Expression.Convert(getProperty, typeof(object));
return Expression.Lambda<Func<object, object>>(castPropertyValue, target).Compile();
}
随后便可使用该函数,来动态调用属性。
Assembly assembly = typeof(Environment).Assembly;
String membername = "FullName";
Object obj = assembly;
Type typ = obj.GetType();
PropertyInfo pi = typ.GetProperty(membername);
Func< object, object> f = CreateGetFunction(pi);
rt = f(obj);
Debug.WriteLine(rt);
可是,该办法还是遇到了异常。
System.TypeAccessException: 方法“DynamicClass.lambda_method(System.Runtime.CompilerServices.Closure, System.Object)”访问类型“System.Reflection.RuntimeAssembly”的尝试失败。
位于 lambda_method(Closure, Object)
位于 LibShared.LibSharedUtil.GetPropertyValue(Type typ, Object obj, String membername, Boolean & ishad)
4.2 解决Expression Tree法的问题
难道真的没办法了吗?
这时突然想起 RuntimeAssembly 是一个内部类(internal),而内部类应该是不能在程序集外访问的。
于是修改了访问代码,通过基类 Assembly 来访问 FullName。
Assembly assembly = typeof(Environment).Assembly;
String membername = "FullName";
Object obj = assembly;
Type typ = typeof(Assembly); // 强制指定基类。不能用 `obj.GetType()` ,因它返回的是 RuntimeAssembly 这个内部类。
PropertyInfo pi = typ.GetProperty(membername);
Func< object, object> f = CreateGetFunction(pi);
rt = f(obj);
Debug.WriteLine(rt);
此时终于能正常的读出FullName属性的值了。
随后尝试反射时也强制指定基类(Assembly),但仍是报异常。可能是它内部限制了。
而且还尝试了用Expression Tree访问关键代码(SecurityCritical)的内容,希望能突破安全限制。可是还是遇到了MethodAccessException异常,看来安全性很严格,没有漏洞。
五、经验总结
- 在 Silverlight中,安全关键(SecuritySafeCritical)的成员无法反射。而且当实际类中有安全关键(SecuritySafeCritical)特性时(如RuntimeAssembly),即使是通过安全透明的基类(如Assembly)来反射,也是不行的。
- 当无法通过反射动态获取属性时,可考虑动态生成代码的方案(IL Emit、Expression Tree)。
- 对于内部类(internal),是无法突破安全限制来访问的,即使是反射、动态生成代码也不行。此时可考虑通过基类来访问。
- 对于关键代码(SecurityCritical),是无法突破安全限制来访问的,即使是反射、动态生成代码也不行。
源码地址:
https://github.com/zyl910/vscsprojects/tree/master/vs14_2015/Silverlight/TestSilverlight
参考文献
- MSDN《安全透明的代码,级别 2》. https://msdn.microsoft.com/zh-cn/library/azure/dd233102.aspx
- Artech《三种属性操作性能比较:PropertyInfo + Expression Tree + Delegate.CreateDelegate》. http://www.cnblogs.com/artech/archive/2011/03/26/Propertyaccesstest.html
- ILSpy . http://www.ilspy.net
[C#] 解决Silverlight反射安全关键(SecuritySafeCritical)时报“System.MethodAccessException: 安全透明方法 XXX 无法使用反射访问”的问题的更多相关文章
- 解决cocos2dx在Xcode中运行时报:convert: iCCP: known incorrect sRGB profile 的问题
解决cocos2dx在Xcode中运行时报:convert: iCCP: known incorrect sRGB profile 的问题 本文的实践来源是参照了两个帖子完成的: http://dis ...
- 解决tomcat部署多个虚拟机时报IllegalStateException: Web app root system property already set to 的问题
解决tomcat部署多个虚拟机时报IllegalStateException: Web app root system property already set to 的问题 在web.xml中添加如 ...
- WPF中实例化Com组件,调用组件的方法时报System.Windows.Forms.AxHost+InvalidActiveXStateException的异常
WPF中实例化Com组件,调用组件的方法时报System.Windows.Forms.AxHost+InvalidActiveXStateException的异常 在wpf中封装Com组件时,调用组件 ...
- 深入理解Java中的反射机制和使用原理!详细解析invoke方法的执行和使用
反射的概念 反射: Refelection,反射是Java的特征之一,允许运行中的Java程序获取自身信息,并可以操作类或者对象的内部属性 通过反射,可以在运行时获得程序或者程序中的每一个类型的成员活 ...
- 解决“只能通过Chrome网上应用商店安装该程序”的方法
摘要 : 最近有些用户反映某个Chrome插件在安装的时候,提示"只能通过Chrome网上应用商店安装该程序",为了解决这一问题,Chrome插件网带来了相关的解决方法. 某些用户 ...
- 通过改变计算机策略来解决“只能通过Chrome网上应用商店安装该程序”的方法及模版文件下载
通过改变计算机策略来解决“只能通过Chrome网上应用商店安装该程序”的方法及模版文件下载 操作步骤 1.开始 -> 运行 -> 输入gpedit.msc -> 回车确定打开计算机本 ...
- 解决C#程序只允许运行一个实例的几种方法详解
解决C#程序只允许运行一个实例的几种方法详解 本篇文章是对C#中程序只允许运行一个实例的几种方法进行了详细的分析介绍,需要的朋友参考下 本文和大家讲一下如何使用C#来创建系统中只能有该程序的一个实例运 ...
- [C#] .NET Core/Standard 1.X 项目中如何使用XmlIgnoreAttribute等标准范围外的内容,兼谈如何解决“violation of security transparency rules failed”(违反安全透明规则失败)异常
作者: zyl910 一.缘由 XML序列化是一个很常用的功能,但对于.NET Core/Standard,其直到2.0版才内置支持XML序列化.具体来说, .NET Core 2.0 或 .NET ...
- C#利用反射来判断对象是否包含某个属性的实现方法
本文实例展示了C#利用反射来判断对象是否包含某个属性的实现方法,对于C#程序设计人员来说有一定的学习借鉴价值. 具体实现代码如下: /// <summary> /// 利用反射来判断对象是 ...
随机推荐
- LoadRunner录制协议的选择
1.选择协议(提高测试结果的准确性) New Single Protocol Script:单协议脚本,选择一种主协议进行测试 New Multiple Protocol Script:多协议脚本,选 ...
- 使用soupUI做接口测试
第一步:点击“file”,选择测试项目采用的协议:(这里我们测试的是http协议的,所以选择第三项) 第二步:在弹窗中输入测试项目的接口URL,并点击“OK”: 第三步:设置和填写请求项的内容并点 ...
- 15行Python 仿百度搜索引擎
开发工具:PyCharm 开发环境:python3.6 + flask + requests 开发流程: 1. 启动一个web服务 from flask import Flask app = Flas ...
- HDU-2032解题报告
Hdu-2032解题报告题意:实现给定行数的杨辉三角的输出. 杨辉三角的特点:每一行数据的开头和结尾是1,然后其他的数据是由其上一个数据与其左上角的数据之和组成11 11 2 11 3 3 11 4 ...
- Java 之 Web前端(三)
1.JSP a.全称:Java Server Page b.运行:翻译.编译.类装载.类实例化.(初始化.服务.销毁 (这三点为Servlet的生命周期)) 2.JSP的基本组成 a.HTML模板 注 ...
- Jenkins环境搭建(5)-与Jmeter完成参数化构建和构建前删除操作
此前介绍过几篇关于Jenkins配置相关的文章,今天再来说说参数化构建和构建前删除已有的报告.在实际测试过程中,是需要测试几套环境的,不使用参数化构建的话,构建脚本就比较麻烦了:自然,已生成的报告,不 ...
- python request 库
快速上手 迫不及待了吗?本页内容为如何入门Requests提供了很好的指引.其假设你已经安装了Requests.如果还没有, 去 安装 一节看看吧. 首先,确认一下: Requests 已安装 Req ...
- SpringBoot+Mybatis+MySql学习
介绍一下SpringBoot整合mybatis,数据库选用的是mysql. 首先创建数据库 CREATE DATABASE test; 建表以及插入初始数据(sql是从navicat中导出的) SET ...
- OSPF补全计划-0 preface
哇靠,一看日历吓了我一跳,我这一个月都没写任何东西,好吧,事情的确多了点儿,同事离职,我需要处理很多untechnical的东西,弄得我很烦,中间学的一点小东西(关于Linux的)也没往这里记,但是我 ...
- 洛谷.3808/3796.[模板]AC自动机
题目链接:简单版,增强版 简单版: #include <cstdio> #include <cstring> const int N=1e6+5,S=26; char s[N] ...