逐步解說:在部分信任案例中發出程式碼
反映發出在完全或部分信任都使用相同的 API 集,但部分功能在部分信任的程式碼中需要特殊的使用權限。 此外,反映發出具有匿名裝載動態方法,這項功能是設計與部分信任搭配使用,以供安全性透明的組件使用。
注意事項 |
---|
在 .NET Framework 3.5 版以前的版本中,發出程式碼需要具有 ReflectionPermissionFlag.ReflectionEmit 旗標的 ReflectionPermission。此權限預設包含在 FullTrust 和 Intranet 具名使用權限集合中,但不在 Internet 使用權限集合中。因此,要從部分信任使用程式庫,程式庫就必須具有 SecurityCriticalAttribute 屬性,而且也執行 ReflectionEmit 的 Assert 方法。這類程式庫需要經審慎的安全性檢閱,因為程式碼錯誤可能會產生安全性漏洞。.NET Framework 3.5 允許程式碼在部分信任案例中發出,而且不發出任何安全性要求,因為產生程式碼本身並非特殊權限操作。也就是說,產生的程式碼不會有比發出它的組件更大的使用權限。這可讓發出程式碼的程式庫成為安全性透明,而無須判斷提示 (Assert) ReflectionEmit,因此撰寫安全的程式庫就不需要如此審慎的安全性檢閱。 |
這個逐步解說將說明下列工作:
設定簡單沙箱來測試部分信任的程式碼。
重要事項 這是在部分信任中實驗程式碼的簡單方式。若要執行實際來自未受信任位置的程式碼,請參閱 HOW TO:在沙箱中執行部分信任的程式碼。
在部分信任應用程式定義域中執行程式碼
使用匿名裝載動態方法,在部分信任中發出和執行程式碼
如需在部分信任案例中發出程式碼的詳細資訊,請參閱反映發出中的安全性問題。
如需這些程序所示範的程式碼完整清單,請參閱本逐步解說最後的範例一節。
設定部分信任位置
下列兩個程序說明如何設定位置,讓您可以使用部分信任來測試程式碼。
第一個程序說明如何建立沙箱應用程式定義域,讓程式碼獲得網際網路使用權限。
第二個程序說明如何將具有 ReflectionPermissionFlag.RestrictedMemberAccess 旗標的 ReflectionPermission 加入至部分信任的應用程式定義域,以便存取具有相同或更低信任權限的組件中的私用資料。
建立沙箱應用程式定義域
若要建立應用程式定義域,讓組件以部分信任權限執行,您必須使用 AppDomain.CreateDomain(String, Evidence, AppDomainSetup, PermissionSet, StrongName[]) 方法多載建立應用程式定義域,指定要授與組件的使用權限集合。 指定授權集最簡單的方式就是從安全性原則擷取具名使用權限集合。
下列程序會建立沙箱應用程式定義域,在其中以部分信任權限執行您的程式碼,以測試發出的程式碼只能存取公用型別之公用成員的案例。 緊接在後面的程序會說明如何加入 RestrictedMemberAccess,以測試發出的程式碼可存取授與相同或更低使用權限的組件中之非公用型別和成員的案例。
若要建立部分信任的應用程式定義域
建立要授與沙箱應用程式定義域中之組件的使用權限集合。 在此例中,會使用網際網路區域的使用權限集合。
Dim ev As New Evidence() ev.AddHostEvidence(new Zone(SecurityZone.Internet)) Dim pset As New NamedPermissionSet("Internet", SecurityManager.GetStandardSandbox(ev))
Evidence ev = new Evidence(); ev.AddHostEvidence(new Zone(SecurityZone.Internet)); PermissionSet pset = new NamedPermissionSet("Internet", SecurityManager.GetStandardSandbox(ev));
建立 AppDomainSetup 物件,以應用程式路徑初始化應用程式定義域。
重要事項 為了容易說明,這個程式碼範例會使用目前的資料夾。若要執行實際來自網際網路的程式碼,請針對未受信任的程式碼使用個別的資料夾,如 HOW TO:在沙箱中執行部分信任的程式碼中所述。
Dim adSetup As New AppDomainSetup() adSetup.ApplicationBase = "."
AppDomainSetup adSetup = new AppDomainSetup(); adSetup.ApplicationBase = ".";
建立應用程式定義域,並針對在應用程式定義域中執行的所有組件指定應用程式定義域安裝資訊以及授權集。
Dim ad As AppDomain = AppDomain.CreateDomain("Sandbox", ev, adSetup, pset, Nothing)
AppDomain ad = AppDomain.CreateDomain("Sandbox", ev, adSetup, pset, null);
AppDomain.CreateDomain(String, Evidence, AppDomainSetup, PermissionSet, StrongName[]) 方法多載的最後一個參數可讓您指定授與完全信任的組件集合,而非應用程式定義域的授權集。 您不需要指定應用程式使用的 .NET Framework 組件,因為這些組件位於全域組件快取中。 全域組件快取中的組件永遠是完全信任的。 您可以使用此參數指定不在全域組件快取中的強式名稱組件。
加入 RestrictedMemberAccess 至沙箱化定義域
主應用程式可允許匿名裝載動態方法存取信任層級與發出程式碼之組件相同或更低的組件中的私用資料。 為了讓此限制能力略過 Just-In-Time (JIT) 可視性檢查,主應用程式會將具有 ReflectionPermissionFlag.RestrictedMemberAccess (RMA) 旗標的 ReflectionPermission 物件加入到授權集。
例如,主應用程式可能會將網際網路使用權限加上 RMA 授與網際網路應用程式,讓網際網路應用程式發出存取其本身組件內私用資料的程式碼。 由於存取限於有相同或更低信任權限的組件,網際網路應用程式無法存取完全信任組件 (如 .NET Framework 組件) 的成員。
注意事項 |
---|
為防止權限升級,當建構匿名裝載動態方法時,會包含發出組件的堆疊資訊。當叫用方法時,就會檢查堆疊資訊。如此一來,從完全信任程式碼叫用的匿名裝載動態方法,仍會限制在發出組件的信任層級。 |
若要建立部分信任加上 RMA 的應用程式定義域
建立具有 RestrictedMemberAccess (RMA) 旗標的新 ReflectionPermission 物件,然後使用 PermissionSet.SetPermission 方法將該使用權限加入到授權集。
pset.SetPermission( _ New ReflectionPermission( _ ReflectionPermissionFlag.RestrictedMemberAccess))
pset.SetPermission( new ReflectionPermission( ReflectionPermissionFlag.RestrictedMemberAccess));
如果授權集還沒有該使用權限,AddPermission 方法會加入使用權限。 如果權限集已包含該使用權限,則會將指定的旗標加入到現有的使用權限。
注意事項 RMA 是匿名裝載動態方法的功能。當一般動態方法略過 JIT 可視性檢查時,發出的程式碼就需要完全信任。
建立應用程式定義域,並指定應用程式定義域安裝資訊以及授權集。
ad = AppDomain.CreateDomain("Sandbox2", ev, adSetup, pset, Nothing)
ad = AppDomain.CreateDomain("Sandbox2", ev, adSetup, pset, null);
在沙箱應用程式定義域中執行程式碼
下列程序說明如何使用可在應用程式定義域中執行的方法定義類別、如何在定義域中建立類別的執行個體,以及如何執行其方法。
若要在應用程式定義域中定義和執行方法
定義從 MarshalByRefObject 衍生的類別 這可讓您在其他應用程式定義域中建立類別的執行個體,以及跨應用程式定義域界限呼叫方法。 在這個範例中,此類別命名為 Worker。
Public Class Worker Inherits MarshalByRefObject
public class Worker : MarshalByRefObject {
定義公用方法,其中包含要執行的程式碼。 在這個範例中,程式碼會發出簡單的動態方法、建立委派來執行方法,並叫用委派。
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(); }
在主程式中,取得組件的顯示名稱。 當您在沙箱應用程式定義域中建立 Worker 類別的執行個體時,會用到這個名稱。
Dim asmName As String = [Assembly].GetExecutingAssembly().FullName
String asmName = Assembly.GetExecutingAssembly().FullName;
在主程式中,建立沙箱應用程式定義域,如本逐步解說的第一個程序所述。 您不需要在 Internet 使用權限集合加入任何使用權限,因為 SimpleEmitDemo 方法只使用公用方法。
在主程式中,於沙箱應用程式定義域中建立 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"。
加入呼叫 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, 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);
限制在於,此匿名裝載動態方法只能存取信任層級與發出組件相同或更低的組件中的私用資料。 例如,如果動態方法使用網際網路信任權限執行,它就可以存取其他也使用網際網路信任權限執行之組件中的私用資料,但無法存取 .NET Framework 組件的私用資料。 .NET Framework 組件會安裝在全域組件快取中,而且永遠是完全信任的。
匿名裝載動態方法只有在主應用程式授與具有 ReflectionPermissionFlag.RestrictedMemberAccess 旗標的 ReflectionPermission 時,才能使用此限制能力略過 JIT 可視性檢查。 當叫用方法時,就會要求此使用權限。
注意事項 當建構動態方法時,會包含發出組件的呼叫堆疊資訊。因此,會對發出組件要求使用權限,而不是叫用方法的組件。這樣可以防止發出的程式碼以升級使用權限執行。
本逐步解說最後的完整程式碼範例示範限制成員存取的用法與限制。 其中的 Worker 類別包含可建立匿名裝載動態方法的方法,不論匿名裝載動態方法是否能在受限情況下略過可視性檢查,而且範例也顯示在具有不同信任層級的應用程式定義域中執行此方法的結果。
注意事項 在受限情況下略過可視性檢查,是匿名裝載動態方法的一項功能。當一般動態方法略過 JIT 可視性檢查時,它們必須被授與完全信任。
範例
說明
下列程式碼範例示範 RestrictedMemberAccess 旗標的用法,其允許匿名裝載動態方法略過 JIT 可視性檢查,但目標成員的信任層級必須與發出程式碼的組件相同或更低。
這個範例定義了一個 Worker 類別,此類別可跨應用程式定義域界限封送處理。 此類別有兩個發出和執行動態方法的 AccessPrivateMethod 方法多載。 第一個多載會發出動態方法來呼叫 Worker 類別的私用 PrivateMethod 方法,而且可發出具有或沒有 JIT 可視性檢查的動態方法。 第二個多載會發出動態方法來存取 String 類別的 internal 屬性 (在 Visual Basic 為 Friend 屬性)。
這個範例會使用 Helper 方法來建立限制為 Internet 使用權限的授權集,然後建立應用程式定義域,方式是使用 AppDomain.CreateDomain(String, Evidence, AppDomainSetup, PermissionSet, StrongName[]) 方法多載指定在該定義域中執行的所有程式碼都使用此授權集。 這個範例會在應用程式定義域中建立 Worker 類別的執行個體,然後執行 AccessPrivateMethod 方法兩次。
AccessPrivateMethod 方法第一次執行時,會強制執行 JIT 可視性檢查。 動態方法在叫用時會失敗,因為 JIT 可視性檢查使其無法存取私用方法。
AccessPrivateMethod 方法第二次執行時,會略過 JIT 可視性檢查。 動態方法在編譯時會失敗,因為 Internet 授權集沒有授與足夠的使用權限來略過可視性檢查。
此範例會將具有 ReflectionPermissionFlag.RestrictedMemberAccess 的 ReflectionPermission 加入至授權集。 範例接著會建立第二個定義域,指定將新授權集中的使用權限授與該定義域中執行的所有程式碼。 這個範例會在新的應用程式定義域中建立 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 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.
'
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 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.
*/
編譯程式碼
- 如果您在 Visual Studio 中建置這個程式碼範例,必須變更類別的名稱,以加入傳遞到 CreateInstanceAndUnwrap 方法的命名空間。 依預設,命名空間是專案的名稱。 例如,如果專案是 "PartialTrust",類別名稱就必須是 "PartialTrust.Worker"。