演练:从部分信任的方案中发出代码

更新:2007 年 11 月

反射发出在完全信任或部分信任的情况下使用相同的 API 集,但有些功能在部分受信任的代码中需要特殊权限。此外,反射发出还具有一种功能 - 匿名承载的动态方法,该功能旨在供安全透明的程序集在部分信任的情况下使用。

说明:

在 .NET Framework 3.5 版 以前,发出代码需要具有 ReflectionPermissionFlag.ReflectionEmit 标志的 ReflectionPermission。默认情况下此权限包含在 FullTrust 和 Intranet 命名权限集中,但不在 Internet 权限集中。因此,仅当库具有 SecurityCriticalAttribute 属性并对 ReflectionEmit 执行了 Assert 方法时,才可以在部分信任的情况下使用该库。这种库需要进行仔细的安全检查,因为编码错误可能会导致安全漏洞。.NET Framework 3.5 允许在部分信任的方案中发出代码而无需发出任何安全请求,因为生成代码本身不是一项特权操作。也就是说,生成的代码不会具有比发出它的程序集更多的权限。这使得发出代码的库是安全透明的,且不再需要断言 ReflectionEmit,这样一来,编写安全的库就不需要进行彻底的安全检查了。

本演练阐释以下任务:

  • 设置部分受信任的环境以测试代码。

  • 在部分受信任的应用程序域中运行代码。

  • 使用匿名承载的动态方法在部分信任的情况下发出和执行代码。

有关在部分信任的方案中发出代码的更多信息,请参见反射发出中的安全问题

有关这些过程中所显示的完整代码清单,请参见本演练结尾处的“示例”一节。

设置部分受信任的位置

下面的过程演示如何设置代码在其中可以在部分信任下运行的位置。

  • 第一个过程演示如何创建沙盒应用程序域,在该应用程序域中代码可以在 Internet 信任下执行。它还说明了一种常见缺陷。

  • 第二个过程演示如何将具有 ReflectionPermissionFlag.RestrictedMemberAccess 标志的 ReflectionPermission 添加到部分受信任的应用程序域中,以便允许访问具有相同或更低信任的程序集中的私有数据。

  • 第三个过程演示如何创建一个将信任级别与文件夹相关联的代码组,使得位于该文件夹中的任何程序集都在部分信任下执行。

除了这些过程外,在简单的方案中,可以使用程序集属性(如以下程序集属性)执行程序集而无需 SkipVerification 权限。您可以通过类似方式使用属性来拒绝 MemberAccess 权限。

<Assembly:SecurityPermissionAttribute(SecurityAction.RequestRefuse, Flags:=SecurityPermissionFlag.SkipVerification)>
[assembly:SecurityPermissionAttribute(SecurityAction.RequestRefuse, Flags=SecurityPermissionFlag.SkipVerification)]

创建沙盒应用程序域

若要创建您的程序集在其中在部分信任下运行的应用程序域,您必须通过使用 AppDomain.CreateDomain(String, Evidence, AppDomainSetup, PermissionSet, array<StrongName[]) 方法重载创建应用程序域来指定要向这些程序集授予的权限集。指定授予集的最简单方法是从安全策略中检索命名权限集。

警告:

不能通过仅指定证据来创建沙盒应用程序域。必须指定授予集或应用程序域策略级别。(设置应用程序域策略级别不在本主题讨论范围内。) 例如,如果您使用具有 Internet 证据的 CreateDomain(String, Evidence) 方法重载,则权限只能在应用程序域边界上强制执行。在应用程序域内部,根据标准安全策略向程序集授予权限。对于您计算机中的控制台应用程序,这将是完全信任。

下面的过程创建运行您的部分信任代码的沙盒应用程序域,以便测试发出的代码在其中只能访问公共类型的公共成员的方案。后面的过程演示如何添加 RestrictedMemberAccess,以便测试发出的代码可在其中访问被授予相同或更低权限的程序集中非公共类型和成员的方案。

创建部分信任的应用程序域

  1. 使用以下 Helper 函数从安全策略中获取命名权限集。

    Private Shared Function GetNamedPermissionSet(ByVal name As String) As PermissionSet 
    
        If (String.IsNullOrEmpty(name)) Then 
            Throw New ArgumentException("name", "Cannot search for a permission set without a name.")
        End If
    
        Dim foundName As Boolean = False
        Dim setIntersection As New PermissionSet(PermissionState.Unrestricted)
    
        ' Search all policy levels.
        Dim levelEnumerator As IEnumerator = SecurityManager.PolicyHierarchy()
        While (levelEnumerator.MoveNext())
    
            Dim level As PolicyLevel = levelEnumerator.Current 
            Debug.Assert(level IsNot Nothing)
    
            ' If this policy level has a named permission set with the 
            ' specified name, intersect it with previous levels.
            Dim levelSet As PermissionSet = level.GetNamedPermissionSet(name)
            If (levelSet IsNot Nothing) Then
    
                foundName = True
                setIntersection = setIntersection.Intersect(levelSet)
    
                ' Intersect() returns null for an empty set. If this occurs
                ' at any point, the resulting permission set is empty.
                If (setIntersection Is Nothing) Then
                    Return New PermissionSet(PermissionState.None)
                End If
            End If
        End While
    
        If Not foundName Then
            setIntersection = New PermissionSet(PermissionState.None)
        End If
        Return setIntersection
    
    End Function 
    
    private static PermissionSet GetNamedPermissionSet(string name)
    {
        if (String.IsNullOrEmpty(name))
            throw new ArgumentException("name", "Cannot search for a permission set without a name.");
    
        bool foundName = false;
        PermissionSet setIntersection = new PermissionSet(PermissionState.Unrestricted);
    
        // Search all policy levels.
        IEnumerator levelEnumerator = SecurityManager.PolicyHierarchy();
        while (levelEnumerator.MoveNext())
        {
            PolicyLevel level = levelEnumerator.Current as PolicyLevel;
            Debug.Assert(level != null);
    
            // If this policy level has a named permission set with the 
            // specified name, intersect it with previous levels.
            PermissionSet levelSet = level.GetNamedPermissionSet(name);
            if (levelSet != null)
            {
                foundName = true;
                setIntersection = setIntersection.Intersect(levelSet);
    
                // Intersect() returns null for an empty set. If this occurs
                // at any point, the resulting permission set is empty.
                if (setIntersection == null)
                    return new PermissionSet(PermissionState.None);
            }
        }
    
        if (!foundName)
            setIntersection = new PermissionSet(PermissionState.None);
    
        return setIntersection;
    }
    

    授予集是在所有策略级别上授予的权限集的交集。也就是说,不会授予某种特定权限,除非该权限在所有策略级别上授予。因此,Helper 函数以完全信任的授予集启动并枚举策略层次结构的级别,使用该授予集与为每个级别定义的权限集的交集。

    说明:

    可以使用 PermissionSet 类创建包含任何权限组合的授予集。

    Helper 函数的用法将在本过程的后面部分演示。

  2. 使用安全区域为部分受信任的位置创建证据。本例中将使用 Internet 区域。

    Dim zoneEvidence() As Object = { New Zone(SecurityZone.Internet) }
    Dim internetZone As New Evidence(zoneEvidence, zoneEvidence)
    
    Object[] zoneEvidence = { new Zone(SecurityZone.Internet) };
    Evidence internetZone = new Evidence(zoneEvidence, zoneEvidence);
    
  3. 创建一个 AppDomainSetup 对象,以便初始化具有应用程序路径的应用程序域。此代码示例使用当前的文件夹。

    Dim adSetup As New AppDomainSetup()
    adSetup.ApplicationBase = "."
    
    AppDomainSetup adSetup = new AppDomainSetup();
    adSetup.ApplicationBase = ".";
    
  4. 使用 Helper 函数从系统策略中检索命名权限集。

    Dim internetSet As PermissionSet = GetNamedPermissionSet("Internet")
    
    PermissionSet internetSet = GetNamedPermissionSet("Internet");
    
  5. 创建应用程序域,指定证据、应用程序域设置信息和授予集。

    Dim ad As AppDomain = AppDomain.CreateDomain("ChildDomain1", _
                                                 internetZone, _
                                                 adSetup, _
                                                 internetSet, _
                                                 Nothing)
    
    AppDomain ad = AppDomain.CreateDomain("ChildDomain1", 
                                          internetZone, 
                                          adSetup, 
                                          internetSet, 
                                          null);
    

    AppDomain.CreateDomain(String, Evidence, AppDomainSetup, PermissionSet, array<StrongName[]) 方法重载的最后一个参数可让您指定一组程序集,这些程序集将被授予完全信任而不是授予该应用程序域的授予集。您不必指定您的应用程序使用的 .NET Framework 程序集,因为这些程序集位于全局程序集缓存中。全局程序集缓存中的程序集总是完全受信任。可以使用此参数指定不在全局程序集缓存中的、具有强名称的程序集。

将 RestrictedMemberAccess 添加到沙盒域

宿主应用程序允许匿名承载的动态方法具有程序集中私有数据的访问权限,前提是这些程序集的信任级别不高于发出代码的程序集的信任级别。为了使此受限制的能力跳过实时 (JIT) 可见性检查,宿主应用程序会将具有 ReflectionPermissionFlag.RestrictedMemberAccess (RMA) 标志的 ReflectionPermission 对象添加到授予集中。

例如,宿主可能会向 Internet 应用程序授予 Internet 权限及 RMA,以使 Internet 应用程序可以发出访问其自己的程序集中的私有数据的代码。由于访问限于具有相同或更低信任的程序集,因此 Internet 应用程序无法访问 .NET Framework 程序集等完全受信任的程序集的成员。

说明:

为了防止特权提升,在构造匿名承载的动态方法时会包括发出程序集的堆栈信息。调用该方法时,会检查堆栈信息。因此,从完全受信任的代码中调用的匿名承载的动态方法仍然仅限于发出程序集的信任级别。

创建部分信任的具有 RMA 的应用程序域

  1. 使用 Helper 函数从安全策略中检索 Internet 命名权限集。

    internetSet = GetNamedPermissionSet("Internet")
    
    internetSet = GetNamedPermissionSet("Internet");
    
  2. 创建一个具有 RestrictedMemberAccess 标志的新 ReflectionPermission 对象,并使用 PermissionSet.SetPermission 方法向授予集添加权限。

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

    如果授予集中尚无权限,AddPermission 方法会向其中添加权限。如果授予集中已经包含权限,则指定的标志会添加给现有权限。

    说明:

    RMA 是匿名承载的动态方法的一个功能。当普通的动态方法跳过 JIT 可见性检查时,必须向发出的代码授予具有 ReflectionPermissionFlag.MemberAccess 标志和 RestrictedMemberAccess 标志的 ReflectionPermission

  3. 创建应用程序域,指定证据、应用程序域设置信息和授予集。

    ad = AppDomain.CreateDomain("ChildDomain2", _
                                internetZone, _
                                adSetup, _
                                internetSet, _
                                Nothing)
    
    ad = AppDomain.CreateDomain("ChildDomain2", 
                                internetZone, 
                                adSetup, 
                                internetSet, 
                                null);
    

创建具有受限权限的文件夹

下面的过程演示如何创建一个将 Internet 权限与文件夹相关联的代码组,以及如何将 RestrictedMemberAccess 标志添加到从该文件夹中运行的代码的授予集。

创建具有 Internet 权限的文件夹

  1. 单击“开始”,指向“控制面板”,指向“管理工具”,然后单击“Microsoft .NET Framework 3.5 配置”。您必须具有管理员特权才能使用配置工具。

  2. 在左窗格中“.NET Framework 2.0 配置”之下,依次展开“我的电脑”、“运行库安全策略”、“计算机”、“代码组”和“All_Code”。

  3. 在右窗格中单击“添加子代码组”以运行“创建代码组向导”。

  4. 为代码组提供一个名称,例如“Internet Sandbox”,还可以选择添加一些说明。单击“下一步”。

  5. 在“选择此代码组的条件类型”列表中选择“URL”。在“URL”框中键入要使用的文件夹的完整路径,然后单击“下一步”。例如,可以键入以下内容:

    file://c:/InternetSandbox/*
    
  6. 从“使用现有权限集”列表中选择“Internet”,然后单击“下一步”。

    说明:

    若要指定命名权限集和具有 RestrictedMemberAccess 标志的 ReflectionPermission 对象,请单击“创建新权限集”,然后使用 XML 文件指定自定义权限集。

  7. 单击“完成”以创建代码组。

  8. 将要在受限信任下运行的程序集放入您在步骤 5 中指定的文件夹内。

在沙盒应用程序域内运行代码

下面的过程说明如何使用可在应用程序域中执行的方法定义一个类,如何在域中创建该类的实例以及如何执行其方法。

在应用程序域中定义和执行方法

  1. 定义一个从 MarshalByRefObject 派生的类。这样,您可以在其他应用程序域中创建该类的实例并跨应用程序域边界进行方法调用。在本示例中,该类被命名为 Worker。

    Public Class Worker
        Inherits MarshalByRefObject
    
    public class Worker : MarshalByRefObject
    {
    
  2. 定义其中包含您要执行的代码的公共方法。在本示例中,代码发出简单的动态方法,创建委托来执行该方法并调用该委托。

    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
    
    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();
    }
    
  3. 在您的主程序中,获取程序集的显示名称。在沙盒应用程序域中创建 Worker 类的实例时要使用此名称。

    Dim asmName As String = [Assembly].GetExecutingAssembly().FullName
    
    String asmName = Assembly.GetExecutingAssembly().FullName;
    
  4. 在您的主程序中,按照本演练的第一个过程中的说明创建沙盒应用程序域。不必向 Internet 权限集中添加任何权限,因为 SimpleEmitDemo 方法仅使用公共方法。

  5. 在您的主程序中,在沙盒应用程序域中创建 Worker 类的一个实例。

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

    CreateInstanceAndUnwrap 方法在目标应用程序域中创建该对象并返回可用来调用该对象的属性和方法的代理。

    说明:

    如果在 Visual Studio 中使用此代码,则必须更改类的名称以包含命名空间。默认情况下,命名空间是项目的名称。例如,如果项目为“PartialTrust”,则类名必须为“PartialTrust.Worker”。

  6. 添加代码以调用 SimpleEmitDemo 方法。该调用跨应用程序域边界封送,并且该代码在沙盒应用程序域中执行。

    w.SimpleEmitDemo()
    
    w.SimpleEmitDemo();
    

使用匿名承载的动态方法

匿名承载的动态方法与系统提供的一个程序集相关联。因此它们独立于其他代码。另一方面,普通的动态方法必须与现有的模块或类型相关联。

说明:

将动态方法与提供匿名承载的程序集相关联的唯一方式是使用以下过程中介绍的构造函数。您不能在匿名承载程序集中显式指定模块。

普通的动态方法可以访问与之关联的模块的内部成员,或者访问与之关联的类型的私有成员。由于匿名承载的动态方法独立于其他代码,因此它们不能访问私有数据。但是,它们的确具有跳过 JIT 可见性检查的受限能力,能够获取对私有数据的访问权限。这种能力仅限于信任级别不高于发出代码的程序集的信任级别的程序集。

为了防止特权提升,在构造匿名承载的动态方法时会包括发出程序集的堆栈信息。调用该方法时,会检查堆栈信息。从完全受信任的代码中调用的匿名承载的动态方法仍然仅限于发出该代码的程序集的信任级别。

使用匿名承载的动态方法

  • 通过使用未指定相关联模块或类型的构造函数来创建匿名承载的动态方法。

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

    如果匿名承载的动态方法仅使用公共类型和方法,则它不需要受限成员访问权限并且不必跳过 JIT 可见性检查。

    发出动态方法不需任何特殊权限,但发出的代码需要其使用的类型和方法所请求的权限。例如,如果发出的代码调用访问某个文件的方法,则该代码需要 FileIOPermission。如果信任级别中未包括该权限,则在执行该发出的代码时会引发安全异常。此处显示的代码发出仅使用 Console.WriteLine 方法的动态方法。因此,该代码可从部分受信任的位置执行。

  • 或者,通过使用 DynamicMethod(String, Type, array<Type[], Boolean) 构造函数并为参数 restrictedSkipVisibility 指定 true 来创建匿名承载的动态方法,该方法具有跳过 JIT 可见性检查的受限能力。

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

    该限制是匿名承载的动态方法只能访问信任级别不高于发出程序集信任级别的程序集中的私有数据。例如,如果动态方法在 Internet 信任下执行,则它可以访问其他同样在 Internet 信任下执行的程序集中的私有数据,但它无法访问 .NET Framework 程序集的私有数据。.NET Framework 程序集安装在全局程序集缓存中并且始终完全受信任。

    仅当宿主应用程序授予具有 ReflectionPermissionFlag.RestrictedMemberAccess 标志的 ReflectionPermission 时,匿名承载的动态方法才可以使用此跳过 JIT 可见性检查的受限能力。对此权限的请求在调用该方法时发出。

    说明:

    在构造动态方法时,将包含发出程序集的调用堆栈信息。因此,请求是针对发出程序集的权限而不是调用该方法的程序集的权限发出的。这样可以阻止发出的代码以提升的权限执行。

    本演练结尾处的完整代码示例演示受限成员访问权限的用法和限制。其 Worker 类包含的方法可以创建具有或没有跳过可见性检查的受限能力的匿名承载的动态方法,并且该示例演示在具有不同信任级别的应用程序域中执行此方法的结果。

    说明:

    跳过可见性检查的受限能力是匿名承载的动态方法的一个功能。当普通的动态方法跳过 JIT 可见性检查时,必须向它们授予具有 ReflectionPermissionFlag.MemberAccess 标志的 ReflectionPermission。此外,访问发出程序集以外的程序集中私有数据的普通动态方法必须具有带 RestrictedMemberAccess 标志的 ReflectionPermission 或者带 SecurityPermissionFlag.ControlEvidence 标志的 SecurityPermission

示例

说明

下面的代码示例演示使用 RestrictedMemberAccess 标志以允许匿名承载的动态方法跳过 JIT 可见性检查,但仅当目标成员的信任级别不高于发出代码的程序集时可行。

该示例定义了一个 Worker 类,该类可以跨应用程序域边界进行封送。该类有两个发出并执行动态方法的 AccessPrivateMethod 方法重载。第一个重载发出调用 Worker 类的私有 PrivateMethod 方法的动态方法,它可以发出跳过或不跳过 JIT 可见性检查的动态方法。第二个重载发出访问 String 类的 internal 属性(在 Visual Basic 中为 Friend 属性)的动态方法。

该示例使用帮助器方法从安全策略获取 Internet 权限集,然后创建一个应用程序域,并使用 AppDomain.CreateDomain(String, Evidence, AppDomainSetup, PermissionSet, array<StrongName[]) 方法重载指定在该域中执行的所有代码都使用此授予集。该示例在应用程序域中创建了 Worker 类的一个实例,并执行 AccessPrivateMethod 方法两次。

  • 第一次执行 AccessPrivateMethod 方法时,强制执行 JIT 可见性检查。动态方法在调用时失败,因为 JIT 可见性检查阻止其访问私有方法。

  • 第二次执行 AccessPrivateMethod 方法时,跳过 JIT 可见性检查。动态方法在编译时失败,因为 Internet 授予集未授予足够的权限跳过可见性检查。

该示例使用帮助器方法获取 Internet 授予集,并将具有 ReflectionPermissionFlag.RestrictedMemberAccessReflectionPermission 添加到授予集。该示例接着又创建了一个域,指定在该域中执行的所有代码都被授予新授予集中的权限。该示例在新应用程序域中创建了 Worker 类的一个实例,并执行 AccessPrivateMethod 方法的两个重载。

  • 执行 AccessPrivateMethod 方法的第一个重载,跳过 JIT 可见性检查。动态方法成功编译并执行,因为发出代码的程序集与包含私有方法的程序集相同。因此,信任级别也是相同的。如果包含 Worker 类的应用程序有若干个程序集,则对于这些程序集中的任何一个来说,同一个进程都会成功,因为它们的信任级别相同。

  • 执行 AccessPrivateMethod 方法的第二个重载,再次跳过 JIT 可见性检查。这一次动态方法在编译时失败,因为它试图访问 String 类的 internal FirstChar 属性。包含 String 类的程序集是完全受信任的。因此,它的信任级别比发出代码的程序集高。

此对比说明 ReflectionPermissionFlag.RestrictedMemberAccess 如何使部分受信任的代码跳过其他部分受信任的代码的可见性检查,而不危及受信任代码的安全。

代码

Imports System
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 = [Assembly].GetExecutingAssembly().FullName

        ' Create evidence for a partially trusted location and a setup object
        ' that specifies the current directory for the application directory.
        Dim zoneEvidence() As Object = { New Zone(SecurityZone.Internet) }
        Dim internetZone As New Evidence(zoneEvidence, zoneEvidence)
        Dim adSetup As New AppDomainSetup()
        adSetup.ApplicationBase = "."

        ' Retrieve the Internet grant set from system policy, and create 
        ' an application domain in which all code that executes is granted
        ' the permissions of an application run from the Internet.
        Dim internetSet As PermissionSet = GetNamedPermissionSet("Internet")
        Dim ad As AppDomain = AppDomain.CreateDomain("ChildDomain1", _
                                                     internetZone, _
                                                     adSetup, _
                                                     internetSet, _
                                                     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. Now create a grant set composed 
        ' of the permissions granted to an Internet application plus
        ' RestrictedMemberAccess, 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)
        internetSet = GetNamedPermissionSet("Internet")
        internetSet.SetPermission( _
            New ReflectionPermission( _
                ReflectionPermissionFlag.RestrictedMemberAccess))
        ad = AppDomain.CreateDomain("ChildDomain2", _
                                    internetZone, _
                                    adSetup, _
                                    internetSet, _
                                    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 


    ' This method retrieves a named permission set from security policy.
    ' The return value is the intersection of all permission sets with the
    ' given name, from all policy levels, or an empty permission set if the
    ' name is not found.
    Private Shared Function GetNamedPermissionSet(ByVal name As String) As PermissionSet 

        If (String.IsNullOrEmpty(name)) Then 
            Throw New ArgumentException("name", "Cannot search for a permission set without a name.")
        End If

        Dim foundName As Boolean = False
        Dim setIntersection As New PermissionSet(PermissionState.Unrestricted)

        ' Search all policy levels.
        Dim levelEnumerator As IEnumerator = SecurityManager.PolicyHierarchy()
        While (levelEnumerator.MoveNext())

            Dim level As PolicyLevel = levelEnumerator.Current 
            Debug.Assert(level IsNot Nothing)

            ' If this policy level has a named permission set with the 
            ' specified name, intersect it with previous levels.
            Dim levelSet As PermissionSet = level.GetNamedPermissionSet(name)
            If (levelSet IsNot Nothing) Then

                foundName = True
                setIntersection = setIntersection.Intersect(levelSet)

                ' Intersect() returns null for an empty set. If this occurs
                ' at any point, the resulting permission set is empty.
                If (setIntersection Is Nothing) Then
                    Return New PermissionSet(PermissionState.None)
                End If
            End If
        End While

        If Not foundName Then
            setIntersection = New PermissionSet(PermissionState.None)
        End If
        Return setIntersection

    End Function 

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 compiled.
'Worker.PrivateMethod()
'MethodAccessException was thrown when the delegate was compiled.
' 
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 = Assembly.GetExecutingAssembly().FullName;

        // Create evidence for a partially trusted location and a setup object
        // that specifies the current directory for the application directory.
        Object[] zoneEvidence = { new Zone(SecurityZone.Internet) };
        Evidence internetZone = new Evidence(zoneEvidence, zoneEvidence);
        AppDomainSetup adSetup = new AppDomainSetup();
        adSetup.ApplicationBase = ".";

        // Retrieve the Internet grant set from system policy, and create 
        // an application domain in which all code that executes is granted
        // the permissions of an application run from the Internet.
        PermissionSet internetSet = GetNamedPermissionSet("Internet");
        AppDomain ad = AppDomain.CreateDomain("ChildDomain1", 
                                              internetZone, 
                                              adSetup, 
                                              internetSet, 
                                              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 compiled.
        w.AccessPrivateMethod(true);


        // Unload the application domain. Now create a grant set composed 
        // of the permissions granted to an Internet application plus
        // RestrictedMemberAccess, 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);
        internetSet = GetNamedPermissionSet("Internet");
        internetSet.SetPermission(
            new ReflectionPermission(
                ReflectionPermissionFlag.RestrictedMemberAccess));
        ad = AppDomain.CreateDomain("ChildDomain2", 
                                    internetZone, 
                                    adSetup, 
                                    internetSet, 
                                    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 method retrieves a named permission set from security policy.
    // The return value is the intersection of all permission sets with the
    // given name, from all policy levels, or an empty permission set if the
    // name is not found.
    private static PermissionSet GetNamedPermissionSet(string name)
    {
        if (String.IsNullOrEmpty(name))
            throw new ArgumentException("name", "Cannot search for a permission set without a name.");

        bool foundName = false;
        PermissionSet setIntersection = new PermissionSet(PermissionState.Unrestricted);

        // Search all policy levels.
        IEnumerator levelEnumerator = SecurityManager.PolicyHierarchy();
        while (levelEnumerator.MoveNext())
        {
            PolicyLevel level = levelEnumerator.Current as PolicyLevel;
            Debug.Assert(level != null);

            // If this policy level has a named permission set with the 
            // specified name, intersect it with previous levels.
            PermissionSet levelSet = level.GetNamedPermissionSet(name);
            if (levelSet != null)
            {
                foundName = true;
                setIntersection = setIntersection.Intersect(levelSet);

                // Intersect() returns null for an empty set. If this occurs
                // at any point, the resulting permission set is empty.
                if (setIntersection == null)
                    return new PermissionSet(PermissionState.None);
            }
        }

        if (!foundName)
            setIntersection = new PermissionSet(PermissionState.None);

        return setIntersection;
    }
}

/* This code example produces the following output:

Hello, World!
MethodAccessException was thrown when the delegate was invoked.
MethodAccessException was thrown when the delegate was compiled.
Worker.PrivateMethod()
MethodAccessException was thrown when the delegate was compiled.
 */

编译代码

  • 如果在 Visual Studio 中生成此代码示例,当您将类传递给 CreateInstanceAndUnwrap 方法时必须更改类的名称以包含命名空间。默认情况下,命名空间是项目的名称。例如,如果项目为“PartialTrust”,则类名必须为“PartialTrust.Worker”。

请参见

任务

如何:运行沙盒中部分受信任的代码

概念

反射发出中的安全问题