演练:在部分信任应用场景中发出代码Walkthrough: Emitting Code in Partial Trust Scenarios

反射发出以完全信任或部分信任形式使用相同的 API 集,但某些功能在部分受信任代码中需要特殊权限。Reflection emit uses the same API set in full or partial trust, but some features require special permissions in partially trusted code. 此外,反射发出具有一个功能,即匿名托管动态方法,旨在由安全透明的程序集采用部分信任的形式使用。In addition, reflection emit has a feature, anonymously hosted dynamic methods, that is designed to be used with partial trust and by security-transparent assemblies.

备注

在 .NET Framework 3.5 之前,发出代码需要带 ReflectionPermissionFlag.ReflectionEmit 标志的 ReflectionPermissionBefore .NET Framework 3.5, emitting code required ReflectionPermission with the ReflectionPermissionFlag.ReflectionEmit flag. 默认情况下,此权限包含在 FullTrustIntranet 命名权限集中,而不在 Internet 权限集中。This permission is included by default in the FullTrust and Intranet named permission sets, but not in the Internet permission set. 因此,只有当库具有 SecurityCriticalAttribute 特性并对 ReflectionEmit 执行了 Assert 方法时,才能在部分信任环境下使用该库。Therefore, a library could be used from partial trust only if it had the SecurityCriticalAttribute attribute and also executed an Assert method for ReflectionEmit. 这种库需要进行仔细的安全检查,因为编码错误可能会导致安全漏洞。Such libraries require careful security review because coding errors could result in security holes. .NET Framework 3.5 允许以部分信任形式发出代码而无需发出任何安全请求,因为生成代码本身不是一项特权操作。The .NET Framework 3.5 allows code to be emitted in partial trust scenarios without issuing any security demands, because generating code is not inherently a privileged operation. 也就是说,生成的代码不会具有比发出它的程序集更多的权限。That is, the generated code has no more permissions than the assembly that emits it. 这使发出代码的库具有安全-透明性且无需断言 ReflectionEmit,因此编写安全库无需彻底的安全检查。This enables libraries that emit code to be security-transparent and removes the need to assert ReflectionEmit, so that writing a secure library does not require such a thorough security review.

本演练阐释了以下任务:This walkthrough illustrates the following tasks:

有关在部分信任方案中发出代码的详细信息,请参阅反射发出中的安全问题For more information about emitting code in partial trust scenarios, see Security Issues in Reflection Emit.

有关这些过程中所显示代码的完整列表,请参阅本演练末的示例部分For a complete listing of the code shown in these procedures, see the Example section at the end of this walkthrough.

设置部分信任的位置Setting up Partially Trusted Locations

以下两个过程显示如何设置可以测试部分信任的代码的位置。The following two procedures show how to set up locations from which you can test code with partial trust.

创建沙盒应用程序域Creating Sandboxed Application Domains

要创建使用部分信任形式运行程序集的应用程序域,必须使用 AppDomain.CreateDomain(String, Evidence, AppDomainSetup, PermissionSet, StrongName[]) 方法重载来指定要授予程序集的权限集,以创建应用程序域。To create an application domain in which your assemblies run with partial trust, you must specify the set of permissions to be granted to the assemblies by using the AppDomain.CreateDomain(String, Evidence, AppDomainSetup, PermissionSet, StrongName[]) method overload to create the application domain. 指定授予集最简单的方法是从安全策略检索命名权限集。The easiest way to specify the grant set is to retrieve a named permission set from security policy.

下列过程创建沙盒应用程序域,它运行部分信任的代码,从而测试方案,方案中发出的代码只能访问公共类型的公共成员。The following procedure creates a sandboxed application domain that runs your code with partial trust, to test scenarios in which emitted code can access only public members of public types. 随后的过程演示如何添加 RestrictedMemberAccess 来测试方案,方案中发出的代码可以访问程序集中的非公共类型和成员(授予了程序集相同权限或较低权限)。A subsequent procedure shows how to add RestrictedMemberAccess, to test scenarios in which emitted code can access nonpublic types and members in assemblies that are granted equal or lesser permissions.

创建部分信任的应用程序域To create an application domain with partial trust

  1. 创建一个权限集,将其授予沙盒应用程序域中的程序集。Create a permission set to grant to the assemblies in the sandboxed application domain. 在这种情况下,使用的是 Internet 区域的权限集。In this case, the permission set of the Internet zone is used.

    Evidence ev = new Evidence();
    ev.AddHostEvidence(new Zone(SecurityZone.Internet));
    PermissionSet pset = new NamedPermissionSet("Internet", SecurityManager.GetStandardSandbox(ev));
    
    Dim ev As New Evidence()
    ev.AddHostEvidence(new Zone(SecurityZone.Internet))
    Dim pset As New NamedPermissionSet("Internet", SecurityManager.GetStandardSandbox(ev))
    
  2. 创建一个 AppDomainSetup 对象,使用应用程序路径初始化应用程序域。Create an AppDomainSetup object to initialize the application domain with an application path.

    重要

    为简单起见,此代码示例使用当前文件夹。For simplicity, this code example uses the current folder. 要运行实际来自 Internet 的代码,请为不受信任的代码使用单独的文件夹,如如何:运行沙盒中部分受信任的代码中所述。To run code that actually comes from the Internet, use a separate folder for the untrusted code, as described in How to: Run Partially Trusted Code in a Sandbox.

    AppDomainSetup adSetup = new AppDomainSetup();
    adSetup.ApplicationBase = ".";
    
    Dim adSetup As New AppDomainSetup()
    adSetup.ApplicationBase = "."
    
  3. 创建应用程序域,为在应用程序域中执行的所有程序集指定应用程序域设置信息和授予集。Create the application domain, specifying the application domain setup information and the grant set for all assemblies that execute in the application domain.

    AppDomain ad = AppDomain.CreateDomain("Sandbox", ev, adSetup, pset, null);
    
    Dim ad As AppDomain = AppDomain.CreateDomain("Sandbox", ev, adSetup, pset, Nothing)
    

    可通过 AppDomain.CreateDomain(String, Evidence, AppDomainSetup, PermissionSet, StrongName[]) 方法重载的最后一个参数来指定要授予完全信任的一组程序集,而不是应用程序域的授予集。The last parameter of the AppDomain.CreateDomain(String, Evidence, AppDomainSetup, PermissionSet, StrongName[]) method overload enables you to specify a set of assemblies that are to be granted full trust, instead of the grant set of the application domain. 无需指定应用程序所使用的 .NET Framework 程序集,因为它们位于全局程序集缓存中。You do not have to specify the .NET Framework assemblies that your application uses, because those assemblies are in the global assembly cache. 全局程序集缓存中的程序集始终是完全受信任的。Assemblies in the global assembly cache are always fully trusted. 可使用此参数指定全局程序集缓存之外的强名称程序集。You can use this parameter to specify strong-named assemblies that are not in the global assembly cache.

将 RestrictedMemberAccess 添加到沙盒域Adding RestrictedMemberAccess to Sandboxed Domains

主机应用程序可允许匿名托管动态方法访问程序集中的专用数据,该程序集具有与发出代码的程序集相同或更低的信任级别。Host applications can allow anonymously hosted dynamic methods to have access to private data in assemblies that have trust levels equal to or less than the trust level of the assembly that emits the code. 要启用此受限能力跳过实时 (JIT) 可见性检查,主机应用程序需使用 ReflectionPermissionFlag.RestrictedMemberAccess (RMA) 标记向授予集添加 ReflectionPermission 对象。To enable this restricted ability to skip just-in-time (JIT) visibility checks, the host application adds a ReflectionPermission object with the ReflectionPermissionFlag.RestrictedMemberAccess (RMA) flag to the grant set.

例如,主机可能授予 Internet 应用程序 Internet 权限和 RMA,以便 Internet 应用程序能发出访问自身程序集中专用数据的代码。For example, a host might grant Internet applications Internet permissions plus RMA, so that an Internet application can emit code that accesses private data in its own assemblies. 由于访问仅限于具有相同和较低信任级别的程序集,因此 Internet 应用程序无法访问完全信任的程序集(如 .NET Framework 程序集)的成员。Because the access is limited to assemblies of equal or lesser trust, an Internet application cannot access members of fully trusted assemblies such as .NET Framework assemblies.

备注

为防止特权提升,在构造匿名托管动态方法时,将包含发出程序集的堆栈信息。To prevent elevation of privilege, stack information for the emitting assembly is included when anonymously hosted dynamic methods are constructed. 调用方法时检查堆栈信息。When the method is invoked, the stack information is checked. 因此,从完全信任的代码调用的匿名托管动态方法仍被限制为发出程序集的信任等级。Thus, an anonymously hosted dynamic method that is invoked from fully trusted code is still limited to the trust level of the emitting assembly.

创建部分信任的应用程序域和 RMATo create an application domain with partial trust plus RMA

  1. 创建一个具有 RestrictedMemberAccess (RMA) 标记的 ReflectionPermission 新对象,并使用 PermissionSet.SetPermission 方法向授予集添加该权限。Create a new ReflectionPermission object with the RestrictedMemberAccess (RMA) flag, and use the PermissionSet.SetPermission method to add the permission to the grant set.

    pset.SetPermission(
        new ReflectionPermission(
            ReflectionPermissionFlag.RestrictedMemberAccess));
    
    pset.SetPermission( _
        New ReflectionPermission( _
            ReflectionPermissionFlag.RestrictedMemberAccess))
    

    如果授予集中尚未包含该权限,则 AddPermission 方法向其中添加该权限。The AddPermission method adds the permission to the grant set if it is not already included. 如果授予集中已包含该权限,则向现有权限添加指定的标记。If the permission is already included in the grant set, the specified flags are added to the existing permission.

    备注

    RMA 是匿名托管动态方法的一项功能。RMA is a feature of anonymously hosted dynamic methods. 当普通动态方法跳过 JIT 可见性检查时,发出的代码要求完全信任。When ordinary dynamic methods skip JIT visibility checks, the emitted code requires full trust.

  2. 创建应用程序域,指定应用程序域设置信息和授予集。Create the application domain, specifying the application domain setup information and the grant set.

    ad = AppDomain.CreateDomain("Sandbox2", ev, adSetup, pset, null);
    
    ad = AppDomain.CreateDomain("Sandbox2", ev, adSetup, pset, Nothing)
    

在沙盒应用程序域中运行代码Running Code in Sandboxed Application Domains

下列过程说明如何使用可在应用程序域中执行的方法来定义类,如何在域中创建类的实例,以及如何执行其方法。The following procedure explains how to define a class by using methods that can be executed in an application domain, how to create an instance of the class in the domain, and how to execute its methods.

在应用程序域中定义和执行方法To define and execute a method in an application domain

  1. 定义一个从 MarshalByRefObject 派生的类。Define a class that derives from MarshalByRefObject. 这使你可在其它应用程序域中创建类的实例,并在应用程序域边界之间进行方法调用。This enables you to create instances of the class in other application domains and to make method calls across application domain boundaries. 此例中将该类命名为 WorkerThe class in this example is named Worker.

    public class Worker : MarshalByRefObject
    {
    
    Public Class Worker
        Inherits MarshalByRefObject
    
  2. 定义包含要执行的代码的公共方法。Define a public method that contains the code you want to execute. 在此示例中,代码发出一个简单的动态方法,创建一个委托来执行方法,并调用该委托。In this example, the code emits a simple dynamic method, creates a delegate to execute the method, and invokes the delegate.

    public void SimpleEmitDemo()
    {
        DynamicMethod meth = new DynamicMethod("", null, null);
        ILGenerator il = meth.GetILGenerator();
        il.EmitWriteLine("Hello, World!");
        il.Emit(OpCodes.Ret);
    
        Test1 t1 = (Test1) meth.CreateDelegate(typeof(Test1));
        t1();
    }
    
    Public Sub SimpleEmitDemo()
    
        Dim meth As DynamicMethod = new DynamicMethod("", Nothing, Nothing)
        Dim il As ILGenerator = meth.GetILGenerator()
        il.EmitWriteLine("Hello, World!")
        il.Emit(OpCodes.Ret)
    
        Dim t1 As Test1 = CType(meth.CreateDelegate(GetType(Test1)), Test1)
        t1()
    End Sub
    
  3. 在主程序中,获取程序集的显示名称。In your main program, get the display name of your assembly. 在沙盒应用程序域中创建 Worker 类的实例时,将用到此名称。This name is used when you create instances of the Worker class in the sandboxed application domain.

    String asmName = typeof(Worker).Assembly.FullName;
    
    Dim asmName As String = GetType(Worker).Assembly.FullName
    
  4. 在主程序中,按本演练过程一中所述,创建一个沙盒应用程序域。In your main program, create a sandboxed application domain, as described in the first procedure in this walkthrough. 无需向 Internet 权限集添加任何权限,因为 SimpleEmitDemo 方法仅使用公共方法。You do not have to add any permissions to the Internet permission set, because the SimpleEmitDemo method uses only public methods.

  5. 在主程序中,在沙盒应用程序域中创建 Worker 类的实例。In your main program, create an instance of the Worker class in the sandboxed application domain.

    Worker w = (Worker) ad.CreateInstanceAndUnwrap(asmName, "Worker");
    
    Dim w As Worker = _
        CType(ad.CreateInstanceAndUnwrap(asmName, "Worker"), Worker)
    

    CreateInstanceAndUnwrap 方法在目标应用程序域中创建该对象,并返回一个可用于调用该对象的属性和方法的代理。The CreateInstanceAndUnwrap method creates the object in the target application domain and returns a proxy that can be used to call the properties and methods of the object.

    备注

    如果在 Visual Studio 中使用该代码,则必须更改类名,使其包括命名空间。If you use this code in Visual Studio, you must change the name of the class to include the namespace. 默认情况下,命名空间是项目的名称。By default, the namespace is the name of the project. 例如,如果项目是“PartialTrust”,那么类名必须为“PartialTrust.Worker”。For example, if the project is "PartialTrust", the class name must be "PartialTrust.Worker".

  6. 添加用于调用 SimpleEmitDemo 方法的代码。Add code to call the SimpleEmitDemo method. 跨应用程序域边界对调用进行封送,并在沙盒应用程序域中执行该代码。The call is marshaled across the application domain boundary, and the code is executed in the sandboxed application domain.

    w.SimpleEmitDemo();
    
    w.SimpleEmitDemo()
    

使用匿名托管动态方法Using Anonymously Hosted Dynamic Methods

匿名托管动态方法与系统提供的透明程序集相关联。Anonymously hosted dynamic methods are associated with a transparent assembly that is provided by the system. 因此,它们所包含的代码是透明的。Therefore, the code they contain is transparent. 普通动态方法则相反,它必须与现有模块(无论是直接指定,还是从关联的类型推断而出)相关联,并从该模块获取安全级别。Ordinary dynamic methods, on the other hand, must be associated with an existing module (whether directly specified or inferred from an associated type), and take their security level from that module.

备注

将动态方法与提供匿名托管的程序集相关联的唯一途径,是使用下列过程中所述的构造函数。The only way to associate a dynamic method with the assembly that provides anonymous hosting is to use the constructors that are described in the following procedure. 在匿名托管程序集中,不可显式指定模块。You cannot explicitly specify a module in the anonymous hosting assembly.

普通动态方法可以访问与之相关联的模块的内部成员,或访问与之相关联的类型的私有成员。Ordinary dynamic methods have access to the internal members of the module they are associated with, or to the private members of the type they are associated with. 由于匿名托管动态方法独立于其他代码,因此它们无需访问专用数据。Because anonymously hosted dynamic methods are isolated from other code, they do not have access to private data. 但是,它们拥有跳过 JIT 可见性检查,访问专用数据的有限能力。However, they do have a restricted ability to skip JIT visibility checks to gain access to private data. 此功能仅限具有与发出代码的程序集相同或较低信任等级的程序集。This ability is limited to assemblies that have trust levels equal to or less than the trust level of the assembly that emits the code.

为防止特权提升,在构造匿名托管动态方法时,将包含发出程序集的堆栈信息。To prevent elevation of privilege, stack information for the emitting assembly is included when anonymously hosted dynamic methods are constructed. 调用方法时检查堆栈信息。When the method is invoked, the stack information is checked. 从完全信任的代码调用的匿名托管动态方法仍被限制为发出该方法的程序集的信任等级。An anonymously hosted dynamic method that is invoked from fully trusted code is still limited to the trust level of the assembly that emitted it.

使用匿名托管动态方法To use anonymously hosted dynamic methods

  • 使用不指定关联模块或类型的构造函数,创建匿名托管动态方法。Create an anonymously hosted dynamic method by using a constructor that does not specify an associated module or type.

    DynamicMethod meth = new DynamicMethod("", null, null);
    ILGenerator il = meth.GetILGenerator();
    il.EmitWriteLine("Hello, World!");
    il.Emit(OpCodes.Ret);
    
    Dim meth As DynamicMethod = new DynamicMethod("", Nothing, Nothing)
    Dim il As ILGenerator = meth.GetILGenerator()
    il.EmitWriteLine("Hello, World!")
    il.Emit(OpCodes.Ret)
    

    如果匿名托管动态方法仅使用公共类型和方法,则它无需受限的成员访问,且无需跳过 JIT 可见性检查。If an anonymously hosted dynamic method uses only public types and methods, it does not require restricted member access and does not have to skip JIT visibility checks.

    发出动态方法无需任何特殊权限,但发出的代码需要它使用的类型和方法所需的权限。No special permissions are required to emit a dynamic method, but the emitted code requires the permissions that are demanded by the types and methods it uses. 例如,如果发出的代码调用访问某个文件的方法,则它需要 FileIOPermissionFor example, if the emitted code calls a method that accesses a file, it requires FileIOPermission. 如果信任级别不包含该权限,那么在执行发出的代码时,将引发安全性异常。If the trust level does not include that permission, a security exception is thrown when the emitted code is executed. 此处显示的代码发出一个动态方法,该方法仅使用 Console.WriteLine 方法。The code shown here emits a dynamic method that uses only the Console.WriteLine method. 因此,可以从部分信任的位置执行该代码。Therefore, the code can be executed from partially trusted locations.

  • 或者,使用 DynamicMethod(String, Type, Type[], Boolean) 构造函数并为 restrictedSkipVisibility 参数指定 true,从而创建一个匿名托管动态方法,该方法具有跳过 JIT 可见性检查的受限能力。Alternatively, create an anonymously hosted dynamic method with restricted ability to skip JIT visibility checks, by using the DynamicMethod(String, Type, Type[], Boolean) constructor and specifying true for the restrictedSkipVisibility parameter.

    DynamicMethod meth = new DynamicMethod("",
                                           typeof(char),
                                           new Type[] { typeof(String) },
                                           true);
    
    Dim meth As New DynamicMethod("", _
                                  GetType(Char), _
                                  New Type() {GetType(String)}, _
                                  True)
    

    限制在于,匿名托管动态方法仅可访问具有与发出程序集相同或比其更低的信任级别的程序集中的专用数据。The restriction is that the anonymously hosted dynamic method can access private data only in assemblies with trust levels equal to or less than the trust level of the emitting assembly. 例如,如果通过 Internet 信任来执行动态方法,则它可以访问同样以 Internet 信任执行的其他程序集中的专用数据,但无法访问 .NET Framework 程序集中的专用数据。For example, if the dynamic method is executing with Internet trust, it can access private data in other assemblies that are also executing with Internet trust, but it cannot access private data of .NET Framework assemblies. .NET Framework 程序集安装在全局程序集缓存中,且始终是完全信任的。.NET Framework assemblies are installed in the global assembly cache and are always fully trusted.

    仅当主机应用程序通过 ReflectionPermissionFlag.RestrictedMemberAccess 标记授予 ReflectionPermission 时,匿名托管动态方法才可使用此受限能力跳过 JIT 可见性检查。Anonymously hosted dynamic methods can use this restricted ability to skip JIT visibility checks only if the host application grants ReflectionPermission with the ReflectionPermissionFlag.RestrictedMemberAccess flag. 调用方法时发出对此权限的请求。The demand for this permission is made when the method is invoked.

    备注

    构造动态方法时,包括发出程序集的调用堆栈信息。Call stack information for the emitting assembly is included when the dynamic method is constructed. 因此,是针对发出程序集的权限提出请求,而不是针对调用方法的程序集提出请求。Therefore, the demand is made against the permissions of the emitting assembly instead of the assembly that invokes the method. 这样可防止使用提升的权限执行发出的代码。This prevents the emitted code from being executed with elevated permissions.

    此演练末的完整代码示例说明了受限成员访问的使用和限制。The complete code example at the end of this walkthrough demonstrates the use and limitations of restricted member access. 它的 Worker 类包括可创建具有或不具有跳过可见性检查这一受限能力的匿名托管动态方法,且示例显示在具有不同信任级别的应用程序域中执行此方法的结果。Its Worker class includes a method that can create anonymously hosted dynamic methods with or without the restricted ability to skip visibility checks, and the example shows the result of executing this method in application domains that have different trust levels.

    备注

    跳过可见性检查这一受限能力是匿名托管动态方法的一种功能。The restricted ability to skip visibility checks is a feature of anonymously hosted dynamic methods. 当普通动态方法跳过 JIT 可见性检查时,向它们授予的信任级别必须是完全信任。When ordinary dynamic methods skip JIT visibility checks, they must be granted full trust.

示例Example

描述Description

下列代码示例演示如何使用 RestrictedMemberAccess 标记允许匿名托管动态方法跳过 JIT 可见性检查,但仅当目标成员与发出代码的程序集具有相同或较低信任级别时才适用。The following code example demonstrates the use of the RestrictedMemberAccess flag to allow anonymously hosted dynamic methods to skip JIT visibility checks, but only when the target member is at an equal or lower level of trust than the assembly that emits the code.

此示例定义了可跨应用程序域边界封送的 Worker 类。The example defines a Worker class that can be marshaled across application domain boundaries. 该类具有两个发出和执行动态方法的 AccessPrivateMethod 方法重载。The class has two AccessPrivateMethod method overloads that emit and execute dynamic methods. 第一个重载发出调用 Worker 类的私有 PrivateMethod 方法的动态方法,而且它发出具有或不具有 JIT 可见性检查的动态方法。The first overload emits a dynamic method that calls the private PrivateMethod method of the Worker class, and it can emit the dynamic method with or without JIT visibility checks. 第二个重载发出访问 String 类的 internal 属性(Visual Basic 中的 Friend 属性)的动态方法。The second overload emits a dynamic method that accesses an internal property (Friend property in Visual Basic) of the String class.

示例使用 Helper 方法创建限制为 Internet 权限的授予集,然后创建应用程序域,使用 AppDomain.CreateDomain(String, Evidence, AppDomainSetup, PermissionSet, StrongName[]) 方法重载来指定域中执行的全部代码均使用此授予集。The example uses a helper method to create a grant set limited to Internet permissions, and then creates an application domain, using the AppDomain.CreateDomain(String, Evidence, AppDomainSetup, PermissionSet, StrongName[]) method overload to specify that all code that executes in the domain uses this grant set. 此示例在应用程序域中创建 Worker 类的一个实例,并执行 AccessPrivateMethod 方法两次。The example creates an instance of the Worker class in the application domain, and executes the AccessPrivateMethod method two times.

  • 首次执行 AccessPrivateMethod 方法时,强制执行 JIT 可见性检查。The first time the AccessPrivateMethod method is executed, JIT visibility checks are enforced. 动态方法将在被调用时失败,这是由于 JIT 可见性检查阻止其访问私有方法。The dynamic method fails when it is invoked, because JIT visibility checks prevent it from accessing the private method.

  • 第二次执行 AccessPrivateMethod 方法时,跳过 JIT 可见性检查。The second time the AccessPrivateMethod method is executed, JIT visibility checks are skipped. 动态方法将在被编译时失败,这是由于 Internet 授予集未授予充足的权限,不能跳过可见性检查。The dynamic method fails when it is compiled, because the Internet grant set does not grant sufficient permissions to skip visibility checks.

此示例向授予集添加具有 ReflectionPermissionFlag.RestrictedMemberAccessReflectionPermissionThe example adds ReflectionPermission with ReflectionPermissionFlag.RestrictedMemberAccess to the grant set. 然后创建第二个域,指定授予在此域中执行的所有代码新授予集中的权限。The example then creates a second domain, specifying that all code that executes in the domain is granted the permissions in the new grant set. 此示例在新应用程序域中创建 Worker 类的一个实例,并执行 AccessPrivateMethod 方法的两个重载。The example creates an instance of the Worker class in the new application domain, and executes both overloads of the AccessPrivateMethod method.

  • 执行 AccessPrivateMethod 方法的第一个重载,跳过 JIT 可见性检查。The first overload of the AccessPrivateMethod method is executed, and JIT visibility checks are skipped. 动态方法将成功编译和执行,这是由于发出代码的程序集与包含私有方法的程序集相同。The dynamic method compiles and executes successfully, because the assembly that emits the code is the same as the assembly that contains the private method. 因此,其信任级别相同。Therefore, the trust levels are equal. 如果包含 Worker 类的应用程序拥有若干程序集,则对于任何一个程序集,相同的进程都将成功,因为它们具有相同的信任级别。If the application that contains the Worker class had several assemblies, the same process would succeed for any one of those assemblies, because they would all be at the same trust level.

  • 执行 AccessPrivateMethod 方法的第二个重载,再次跳过 JIT 可见性检查。The second overload of the AccessPrivateMethod method is executed, and again JIT visibility checks are skipped. 这一次,动态方法在编译时失败,因为它尝试访问 String 类的 internal FirstChar 属性。This time the dynamic method fails when it is compiled, because it tries to access the internal FirstChar property of the String class. 包含 String 类的程序集是完全信任的程序集。The assembly that contains the String class is fully trusted. 因此,它比发出代码的程序集拥有更高的信任级别。Therefore, it is at a higher level of trust than the assembly that emits the code.

此比较显示了 ReflectionPermissionFlag.RestrictedMemberAccess 如何使部分受信任的代码为其它部分受信任的代码跳过可见性检查,而不影响受信任代码的安全性。This comparison shows how ReflectionPermissionFlag.RestrictedMemberAccess enables partially trusted code to skip visibility checks for other partially trusted code without compromising the security of trusted code.

代码Code

using System;
using System.Reflection.Emit;
using System.Reflection;
using System.Security;
using System.Security.Permissions;
using System.Security.Policy;
using System.Collections;
using System.Diagnostics;

// This code example works properly only if it is run from a fully
// trusted location, such as your local computer.

// Delegates used to execute the dynamic methods.
//
public delegate void Test(Worker w);
public delegate void Test1();
public delegate char Test2(String instance);

// The Worker class must inherit MarshalByRefObject so that its public
// methods can be invoked across application domain boundaries.
//
public class Worker : MarshalByRefObject
{
    private void PrivateMethod()
    {
        Console.WriteLine("Worker.PrivateMethod()");
    }

    public void SimpleEmitDemo()
    {
        DynamicMethod meth = new DynamicMethod("", null, null);
        ILGenerator il = meth.GetILGenerator();
        il.EmitWriteLine("Hello, World!");
        il.Emit(OpCodes.Ret);

        Test1 t1 = (Test1) meth.CreateDelegate(typeof(Test1));
        t1();
    }

    // This overload of AccessPrivateMethod emits a dynamic method and
    // specifies whether to skip JIT visiblity checks. It creates a
    // delegate for the method and invokes the delegate. The dynamic
    // method calls a private method of the Worker class.
    public void AccessPrivateMethod(bool restrictedSkipVisibility)
    {
        // Create an unnamed dynamic method that has no return type,
        // takes one parameter of type Worker, and optionally skips JIT
        // visiblity checks.
        DynamicMethod meth = new DynamicMethod(
            "",
            null,
            new Type[] { typeof(Worker) },
            restrictedSkipVisibility);

        // Get a MethodInfo for the private method.
        MethodInfo pvtMeth = typeof(Worker).GetMethod("PrivateMethod",
            BindingFlags.NonPublic | BindingFlags.Instance);

        // Get an ILGenerator and emit a body for the dynamic method.
        ILGenerator il = meth.GetILGenerator();

        // Load the first argument, which is the target instance, onto the
        // execution stack, call the private method, and return.
        il.Emit(OpCodes.Ldarg_0);
        il.EmitCall(OpCodes.Call, pvtMeth, null);
        il.Emit(OpCodes.Ret);

        // Create a delegate that represents the dynamic method, and
        // invoke it.
        try
        {
            Test t = (Test) meth.CreateDelegate(typeof(Test));
            try
            {
                t(this);
            }
            catch (Exception ex)
            {
                Console.WriteLine("{0} was thrown when the delegate was invoked.",
                    ex.GetType().Name);
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("{0} was thrown when the delegate was compiled.",
                ex.GetType().Name);
        }
    }

    // This overload of AccessPrivateMethod emits a dynamic method that takes
    // a string and returns the first character, using a private field of the
    // String class. The dynamic method skips JIT visiblity checks.
    public void AccessPrivateMethod()
    {
        DynamicMethod meth = new DynamicMethod("",
                                               typeof(char),
                                               new Type[] { typeof(String) },
                                               true);

        // Get a MethodInfo for the 'get' accessor of the private property.
        PropertyInfo pi = typeof(System.String).GetProperty(
            "FirstChar",
            BindingFlags.NonPublic | BindingFlags.Instance);
        MethodInfo pvtMeth = pi.GetGetMethod(true);

        // Get an ILGenerator and emit a body for the dynamic method.
        ILGenerator il = meth.GetILGenerator();

        // Load the first argument, which is the target string, onto the
        // execution stack, call the 'get' accessor to put the result onto
        // the execution stack, and return.
        il.Emit(OpCodes.Ldarg_0);
        il.EmitCall(OpCodes.Call, pvtMeth, null);
        il.Emit(OpCodes.Ret);

        // Create a delegate that represents the dynamic method, and
        // invoke it.
        try
        {
            Test2 t = (Test2) meth.CreateDelegate(typeof(Test2));
            char first = t("Hello, World!");
            Console.WriteLine("{0} is the first character.", first);
        }
        catch (Exception ex)
        {
            Console.WriteLine("{0} was thrown when the delegate was compiled.",
                ex.GetType().Name);
        }
    }

    // The entry point for the code example.
    static void Main()
    {
        // Get the display name of the executing assembly, to use when
        // creating objects to run code in application domains.
        String asmName = typeof(Worker).Assembly.FullName;

        // Create the permission set to grant to other assemblies. In this
        // case they are the permissions found in the Internet zone.
        Evidence ev = new Evidence();
        ev.AddHostEvidence(new Zone(SecurityZone.Internet));
        PermissionSet pset = new NamedPermissionSet("Internet", SecurityManager.GetStandardSandbox(ev));

        // For simplicity, set up the application domain to use the
        // current path as the application folder, so the same executable
        // can be used in both trusted and untrusted scenarios. Normally
        // you would not do this with real untrusted code.
        AppDomainSetup adSetup = new AppDomainSetup();
        adSetup.ApplicationBase = ".";

        // Create an application domain in which all code that executes is
        // granted the permissions of an application run from the Internet.
        AppDomain ad = AppDomain.CreateDomain("Sandbox", ev, adSetup, pset, null);

        // Create an instance of the Worker class in the partially trusted
        // domain. Note: If you build this code example in Visual Studio,
        // you must change the name of the class to include the default
        // namespace, which is the project name. For example, if the project
        // is "AnonymouslyHosted", the class is "AnonymouslyHosted.Worker".
        Worker w = (Worker) ad.CreateInstanceAndUnwrap(asmName, "Worker");

        // Emit a simple dynamic method that prints "Hello, World!"
        w.SimpleEmitDemo();

        // Emit and invoke a dynamic method that calls a private method
        // of Worker, with JIT visibility checks enforced. The call fails
        // when the delegate is invoked.
        w.AccessPrivateMethod(false);

        // Emit and invoke a dynamic method that calls a private method
        // of Worker, skipping JIT visibility checks. The call fails when
        // the method is invoked.
        w.AccessPrivateMethod(true);

        // Unload the application domain. Add RestrictedMemberAccess to the
        // grant set, and use it to create an application domain in which
        // partially trusted code can call private members, as long as the
        // trust level of those members is equal to or lower than the trust
        // level of the partially trusted code.
        AppDomain.Unload(ad);
        pset.SetPermission(
            new ReflectionPermission(
                ReflectionPermissionFlag.RestrictedMemberAccess));
        ad = AppDomain.CreateDomain("Sandbox2", ev, adSetup, pset, null);

        // Create an instance of the Worker class in the partially trusted
        // domain.
        w = (Worker) ad.CreateInstanceAndUnwrap(asmName, "Worker");

        // Again, emit and invoke a dynamic method that calls a private method
        // of Worker, skipping JIT visibility checks. This time compilation
        // succeeds because of the grant for RestrictedMemberAccess.
        w.AccessPrivateMethod(true);

        // Finally, emit and invoke a dynamic method that calls an internal
        // method of the String class. The call fails, because the trust level
        // of the assembly that contains String is higher than the trust level
        // of the assembly that emits the dynamic method.
        w.AccessPrivateMethod();
    }
}

/* This code example produces the following output:

Hello, World!
MethodAccessException was thrown when the delegate was invoked.
MethodAccessException was thrown when the delegate was invoked.
Worker.PrivateMethod()
MethodAccessException was thrown when the delegate was compiled.
 */
Imports System.Reflection.Emit
Imports System.Reflection
Imports System.Security
Imports System.Security.Permissions
Imports System.Security.Policy
Imports System.Collections
Imports System.Diagnostics

' This code example works properly only if it is run from a fully 
' trusted location, such as your local computer.

' Delegates used to execute the dynamic methods.
'
Public Delegate Sub Test(ByVal w As Worker)
Public Delegate Sub Test1()
Public Delegate Function Test2(ByVal instance As String) As Char

' The Worker class must inherit MarshalByRefObject so that its public 
' methods can be invoked across application domain boundaries.
'
Public Class Worker
    Inherits MarshalByRefObject

    Private Sub PrivateMethod()
        Console.WriteLine("Worker.PrivateMethod()")
    End Sub

    Public Sub SimpleEmitDemo()

        Dim meth As DynamicMethod = new DynamicMethod("", Nothing, Nothing)
        Dim il As ILGenerator = meth.GetILGenerator()
        il.EmitWriteLine("Hello, World!")
        il.Emit(OpCodes.Ret)

        Dim t1 As Test1 = CType(meth.CreateDelegate(GetType(Test1)), Test1)
        t1()
    End Sub

    ' This overload of AccessPrivateMethod emits a dynamic method and
    ' specifies whether to skip JIT visiblity checks. It creates a 
    ' delegate for the method and invokes the delegate. The dynamic 
    ' method calls a private method of the Worker class.
    Overloads Public Sub AccessPrivateMethod( _
                       ByVal restrictedSkipVisibility As Boolean)

        ' Create an unnamed dynamic method that has no return type,
        ' takes one parameter of type Worker, and optionally skips JIT
        ' visiblity checks.
        Dim meth As New DynamicMethod("", _
                                      Nothing, _
                                      New Type() {GetType(Worker)}, _
                                      restrictedSkipVisibility)

        ' Get a MethodInfo for the private method.
        Dim pvtMeth As MethodInfo = GetType(Worker).GetMethod( _
            "PrivateMethod", _
            BindingFlags.NonPublic Or BindingFlags.Instance)

        ' Get an ILGenerator and emit a body for the dynamic method.
        Dim il As ILGenerator = meth.GetILGenerator()

        ' Load the first argument, which is the target instance, onto the
        ' execution stack, call the private method, and return.
        il.Emit(OpCodes.Ldarg_0)
        il.EmitCall(OpCodes.Call, pvtMeth, Nothing)
        il.Emit(OpCodes.Ret)

        ' Create a delegate that represents the dynamic method, and 
        ' invoke it. 
        Try
            Dim t As Test = CType(meth.CreateDelegate(GetType(Test)), Test)
            Try
                t(Me)
            Catch ex As Exception
                Console.WriteLine("{0} was thrown when the delegate was invoked.", _
                    ex.GetType().Name)
            End Try
        Catch ex As Exception
            Console.WriteLine("{0} was thrown when the delegate was compiled.", _
                ex.GetType().Name)
        End Try

    End Sub


    ' This overload of AccessPrivateMethod emits a dynamic method that takes
    ' a string and returns the first character, using a private field of the 
    ' String class. The dynamic method skips JIT visiblity checks.
    Overloads Public Sub AccessPrivateMethod()

        Dim meth As New DynamicMethod("", _
                                      GetType(Char), _
                                      New Type() {GetType(String)}, _
                                      True)

        ' Get a MethodInfo for the 'get' accessor of the private property.
        Dim pi As PropertyInfo = GetType(String).GetProperty( _
            "FirstChar", _
            BindingFlags.NonPublic Or BindingFlags.Instance)
        Dim pvtMeth As MethodInfo = pi.GetGetMethod(True)

        ' Get an ILGenerator and emit a body for the dynamic method.
        Dim il As ILGenerator = meth.GetILGenerator()

        ' Load the first argument, which is the target string, onto the
        ' execution stack, call the 'get' accessor to put the result onto 
        ' the execution stack, and return.
        il.Emit(OpCodes.Ldarg_0)
        il.EmitCall(OpCodes.Call, pvtMeth, Nothing)
        il.Emit(OpCodes.Ret)

        ' Create a delegate that represents the dynamic method, and 
        ' invoke it. 
        Try
            Dim t As Test2 = CType(meth.CreateDelegate(GetType(Test2)), Test2)
            Dim first As Char = t("Hello, World!")
            Console.WriteLine("{0} is the first character.", first)
        Catch ex As Exception
            Console.WriteLine("{0} was thrown when the delegate was compiled.", _
                ex.GetType().Name)
        End Try

    End Sub
End Class

Friend Class Example

    ' The entry point for the code example.
    Shared Sub Main()

        ' Get the display name of the executing assembly, to use when
        ' creating objects to run code in application domains.
        Dim asmName As String = GetType(Worker).Assembly.FullName

        ' Create the permission set to grant to other assemblies. In this
        ' case they are the permissions found in the Internet zone.
        Dim ev As New Evidence()
        ev.AddHostEvidence(new Zone(SecurityZone.Internet))
        Dim pset As New NamedPermissionSet("Internet", SecurityManager.GetStandardSandbox(ev))

        ' For simplicity, set up the application domain to use the 
        ' current path as the application folder, so the same executable
        ' can be used in both trusted and untrusted scenarios. Normally
        ' you would not do this with real untrusted code.
        Dim adSetup As New AppDomainSetup()
        adSetup.ApplicationBase = "."

        ' Create an application domain in which all code that executes is 
        ' granted the permissions of an application run from the Internet.
        Dim ad As AppDomain = AppDomain.CreateDomain("Sandbox", ev, adSetup, pset, Nothing)

        ' Create an instance of the Worker class in the partially trusted 
        ' domain. Note: If you build this code example in Visual Studio, 
        ' you must change the name of the class to include the default 
        ' namespace, which is the project name. For example, if the project
        ' is "AnonymouslyHosted", the class is "AnonymouslyHosted.Worker".
        Dim w As Worker = _
            CType(ad.CreateInstanceAndUnwrap(asmName, "Worker"), Worker)

        ' Emit a simple dynamic method that prints "Hello, World!"
        w.SimpleEmitDemo()

        ' Emit and invoke a dynamic method that calls a private method
        ' of Worker, with JIT visibility checks enforced. The call fails 
        ' when the delegate is invoked.
        w.AccessPrivateMethod(False)

        ' Emit and invoke a dynamic method that calls a private method
        ' of Worker, skipping JIT visibility checks. The call fails when
        ' the method is compiled.
        w.AccessPrivateMethod(True)


        ' Unload the application domain. Add RestrictedMemberAccess to the
        ' grant set, and use it to create an application domain in which
        ' partially trusted code can call private members, as long as the 
        ' trust level of those members is equal to or lower than the trust 
        ' level of the partially trusted code. 
        AppDomain.Unload(ad)
        pset.SetPermission( _
            New ReflectionPermission( _
                ReflectionPermissionFlag.RestrictedMemberAccess))
        ad = AppDomain.CreateDomain("Sandbox2", ev, adSetup, pset, Nothing)

        ' Create an instance of the Worker class in the partially trusted 
        ' domain. 
        w = CType(ad.CreateInstanceAndUnwrap(asmName, "Worker"), Worker)

        ' Again, emit and invoke a dynamic method that calls a private method
        ' of Worker, skipping JIT visibility checks. This time compilation 
        ' succeeds because of the grant for RestrictedMemberAccess.
        w.AccessPrivateMethod(True)

        ' Finally, emit and invoke a dynamic method that calls an internal 
        ' method of the String class. The call fails, because the trust level
        ' of the assembly that contains String is higher than the trust level
        ' of the assembly that emits the dynamic method.
        w.AccessPrivateMethod()

    End Sub
End Class

' This code example produces the following output:
'
'Hello, World!
'MethodAccessException was thrown when the delegate was invoked.
'MethodAccessException was thrown when the delegate was invoked.
'Worker.PrivateMethod()
'MethodAccessException was thrown when the delegate was compiled.
' 

编译代码Compiling the Code

  • 如果在 Visual Studio 中生成此代码示例,则必须更改类名,以便在将命名空间传递给 CreateInstanceAndUnwrap 方法时包括该命名空间。If you build this code example in Visual Studio, you must change the name of the class to include the namespace when you pass it to the CreateInstanceAndUnwrap method. 默认情况下,命名空间是项目的名称。By default, the namespace is the name of the project. 例如,如果项目是“PartialTrust”,那么类名必须为“PartialTrust.Worker”。For example, if the project is "PartialTrust", the class name must be "PartialTrust.Worker".

请参阅See also