逐步解說:在部分信任案例中發出程式碼

更新:2007 年 11 月

反映發出在完全或部分信任都使用相同的 API 集,但部分功能在部分信任的程式碼中需要特殊的使用權限。此外,反映發出具有匿名裝載動態方法,這項功能是設計與部分信任搭配使用,以供安全性透明的組件使用。

注意事項:

在 .NET Framework 3.5 版以前的版本中,發出程式碼需要具有 ReflectionPermissionFlag.ReflectionEmit 旗標的 ReflectionPermission。此權限預設包含在 FullTrust 和 Intranet 具名使用權限集合中,但不在 Internet 使用權限集合中。因此,要從部分信任使用程式庫,程式庫就必須具有 SecurityCriticalAttribute 屬性,而且也執行 ReflectionEmitAssert 方法。這類程式庫需要經審慎的安全性檢閱,因為程式碼錯誤可能會產生安全性漏洞。.NET Framework 3.5 允許程式碼在部分信任案例中發出而不會發出任何安全性要求,因為產生程式碼本身並非特殊權限操作。也就是說,產生的程式碼不會有比發出它的組件更大的使用權限。這可讓發出程式碼的程式庫成為安全性透明,而無須判斷提示 (Assert) ReflectionEmit,因此撰寫安全的程式庫就不需要如此審慎的安全性檢閱。

此逐步解說會說明以下工作:

  • 設定部分信任環境來測試程式碼.

  • 在部分信任應用程式定義域中執行程式碼

  • 使用匿名裝載動態方法,在部分信任中發出和執行程式碼

如需在部分信任案例中發出程式碼的詳細資訊,請參閱反映發出中的安全性問題

如需這些程序所示範的程式碼完整清單,請參閱本逐步解說最後的範例一節。

設定部分信任位置

下列程序說明如何設定位置,讓程式碼在部分信任的情況下執行。

  • 第一個程序說明如何建立沙箱化應用程式定義域,讓程式碼在網際網路信任的情況下執行。此外也會解釋一個常見的陷阱。

  • 第二個程序說明如何將具有 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[]) 方法多載建立應用程式定義域,指定要授與組件的使用權限集合。指定授權集最簡單的方式就是從安全性原則擷取具名使用權限集合。

警告:

您不能只指定辨識項 (Evidence) 來建立沙箱化應用程式定義域,而必須指定授權集或應用程式定義域原則層級(設定應用程式定義域原則層級不在本主題討論範圍內)。例如,如果您在網際網路辨識項使用 CreateDomain(String, Evidence) 方法多載,使用權限就只會在應用程式定義域界限強制執行。在應用程式定義域內,會根據標準安全性原則授與組件使用權限。若為電腦上的主控台應用程式 (Console Application),這就會是完全信任。

下列程序會建立沙箱化應用程式定義域,在其中以部分信任權限執行您的程式碼,以測試發出的程式碼只能存取公用型別之公用成員的案例。緊接在後面的程序會說明如何加入 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. 使用安全性區域,建立部分信任位置的辨識項。在此例中,會使用網際網路區域。

    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 至沙箱化定義域

主應用程式可允許匿名裝載動態方法存取信任層級與發出程式碼之組件相同或更低的組件中的私用資料。為了讓此限制能力略過 Just-In-Time (JIT) 可視性檢查,主應用程式會將具有 ReflectionPermissionFlag.RestrictedMemberAccess (RMA) 旗標的 ReflectionPermission 物件加入到授權集。

例如,主應用程式可能會將網際網路使用權限加上 RMA 授與網際網路應用程式,讓網際網路應用程式發出存取其本身組件內私用資料的程式碼。由於存取限於有相同或更低信任權限的組件,網際網路應用程式無法存取完全信任組件 (如 .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 可視性檢查時,除了 RestrictedMemberAccess 旗標外,還必須將具有 ReflectionPermissionFlag.MemberAccess 旗標的 ReflectionPermission 授與發出的程式碼。

  3. 建立應用程式定義域,指定辨識項、應用程式定義域安裝資訊以及授權集。

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

建立具有限制權限的資料夾

下列程序說明如何建立程式碼群組,將網際網路使用權限與資料夾建立關聯,以及如何將 RestrictedMemberAccess 旗標加入到從該資料夾執行之程式碼的授權集。

若要建立具有網際網路使用權限的資料夾

  1. 按一下 [開始],依序指向 [控制台]、[系統管理工具],然後按一下 [Microsoft .NET Framework 3.5 組態]。您必須有系統管理員權限才能使用組態工具。

  2. 在左窗格的 [.NET Framework 2.0 組態] 底下,依序展開 [我的電腦]、[執行階段安全性原則]、[電腦]、[程式碼群組] 和 [All_Code]。

  3. 在右窗格中,按一下 [加入子程式碼群組] 執行建立程式碼群組精靈。

  4. 將名稱 (例如 "Internet Sandbox") 和選擇性的描述指定給程式碼群組。按 [下一步]。

  5. 在 [選擇這個程式碼群組的條件類型] 清單中選取 [URL]。在 [URL] 方塊中,輸入所要使用資料夾的完整路徑,然後按 [下一步]。例如,您可以輸入:

    file://c:/InternetSandbox/*
    
  6. 從 [使用現有使用權限集合] 清單選取 [網際網路],然後按 [下一步]。

    注意事項:

    若要指定具名使用權限集合以及具有 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 方法會在目標應用程式定義域中建立物件,並傳回可用來呼叫物件屬性和方法的 Proxy。

    注意事項:

    如果您在 Visual Studio 中使用這段程式碼,必須變更類別的名稱,在其中加入命名空間。依預設,命名空間是專案的名稱。例如,如果專案是 "PartialTrust",類別名稱就必須是 "PartialTrust.Worker"。

  6. 加入呼叫 SimpleEmitDemo 方法的程式碼。此呼叫會跨應用程式定義域界限封送處理,而程式碼會在沙箱化應用程式定義域中執行。

    w.SimpleEmitDemo()
    
    w.SimpleEmitDemo();
    

使用匿名裝載動態方法

匿名裝載動態方法與系統提供的組件相關聯。因此,這些方法是與其他程式碼分開來的。反之,一般動態方法就必須與現有模組或型別相關聯。

注意事項:

要使動態方法與提供匿名裝載的組件產生關聯,唯一方法就是使用下列程序所述的建構函式 (Constructor)。您不能明確指定匿名裝載組件中的模組。

一般動態方法可以存取與其相關聯之模組的內部成員,或相關聯型別的私用成員。由於匿名裝載動態方法與其他程式碼分開,就無法存取私用資料。不過,這些方法可在受限的情況下略過 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);
    

    限制在於,此匿名裝載動態方法只能存取信任層級與發出組件相同或更低的組件中的私用資料。he 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.例如,如果動態方法使用網際網路信任權限執行,可以存取其他也使用網際網路信任權限執行的組件中的私用資料,但無法存取 .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 屬性)。

這個範例使用 Helper 方法從安全性原則取得 Internet 使用權限集合,然後建立應用程式定義域,方式是使用 AppDomain.CreateDomain(String, Evidence, AppDomainSetup, PermissionSet, array<StrongName[]) 方法多載指定在該定義域中執行的所有程式碼都使用此授權集。這個範例會在應用程式定義域中建立 Worker 類別的執行個體,然後執行 AccessPrivateMethod 方法兩次。

  • AccessPrivateMethod 方法第一次執行時,會強制執行 JIT 可視性檢查。動態方法在叫用時會失敗,因為 JIT 可視性檢查使其無法存取私用方法。

  • AccessPrivateMethod 方法第二次執行時,會略過 JIT 可視性檢查。動態方法在編譯時會失敗,因為 Internet 授權集沒有授與足夠的使用權限來略過可視性檢查。

這個範例使用 Helper 方法取得 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"。

請參閱

工作

HOW TO:在沙箱中執行部分信任的程式碼

概念

反映發出中的安全性問題