演练:在部分信任应用场景中发出代码
反射发出在完全信任或部分信任的情况下使用相同的 API 集,但有些功能在部分受信任的代码中需要特殊权限。 此外,反射发出还具有一种功能 - 匿名承载的动态方法,该功能旨在供安全透明的程序集在部分信任的情况下使用。
注意 |
---|
在 .NET Framework 3.5 版以前,发出代码需要具有 ReflectionPermissionFlag.ReflectionEmit 标志的 ReflectionPermission。默认情况下,此权限包含在 FullTrust 和 Intranet 命名权限集中,但不在 Internet 权限集中。因此,仅当库具有 SecurityCriticalAttribute 特性并对 ReflectionEmit 执行了 Assert 方法时,才可以在部分信任的情况下使用该库。此类库需要仔细的安全审查,因为编码错误可能会导致安全漏洞。.NET Framework 3.5 允许在部分信任的方案中发出代码而无需发出任何安全请求,因为生成代码本身不是一项特权操作。也就是说,生成的代码不会具有比发出它的程序集更多的权限。这使得发出代码的库是安全透明的,且不再需要断言 ReflectionEmit,这样一来,编写安全的库就不需要进行彻底的安全检查了。 |
本演练阐释了以下任务:
设置简单沙盒以测试部分信任的代码。
重要事项 这是一种试验处于部分信任模式的代码的简单方法。若要运行实际上来自于不可信位置的代码,请参见如何:运行沙盒中部分受信任的代码。
在部分受信任的应用程序域中运行代码。
使用匿名承载的动态方法在部分信任的情况下发出和执行代码。
有关在部分信任的方案中发出代码的更多信息,请参见反射发出中的安全问题。
有关这些过程中所显示的完整代码清单,请参见本演练结尾处的“示例”一节。
设置部分受信任的位置
下面的两个过程演示如何设置可从中对处于部分信任模式的代码进行测试的位置。
第一个过程演示如何创建一个沙盒应用程序域,其中的代码将被授予 Internet 权限。
第二个过程演示如何将具有 ReflectionPermissionFlag.RestrictedMemberAccess 标志的 ReflectionPermission 添加到一个部分受信任的应用程序域中,以便允许访问具有相同或更低信任的程序集中的私有数据。
创建沙盒应用程序域
若要创建您的程序集在其中在部分信任下运行的应用程序域,您必须通过使用 AppDomain.CreateDomain(String, Evidence, AppDomainSetup, PermissionSet, StrongName[]) 方法重载创建应用程序域来指定要向这些程序集授予的权限集。 指定授予集的最简单方法是从安全策略中检索命名权限集。
下面的过程创建运行您的部分信任代码的沙盒应用程序域,以便测试发出的代码在其中只能访问公共类型的公共成员的方案。 后面的过程演示如何添加 RestrictedMemberAccess,以便测试发出的代码可在其中访问被授予相同或更低权限的程序集中非公共类型和成员的方案。
创建部分信任的应用程序域
创建一个权限集以将其授予沙盒应用程序域中的程序集。 在此例中,将使用 Internet 区域的权限集。
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 对象,以便初始化具有应用程序路径的应用程序域。
重要事项 为简单起见,此代码示例使用当前的文件夹。若要运行实际上来自于 Internet 的代码,请对不受信任的代码使用单独的文件夹,如如何:运行沙盒中部分受信任的代码中所述。
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 添加到沙盒域
宿主应用程序允许匿名承载的动态方法具有程序集中私有数据的访问权限,前提是这些程序集的信任级别不高于发出代码的程序集的信任级别。 为了使此受限制的能力跳过实时 (JIT) 可见性检查,宿主应用程序会将具有 ReflectionPermissionFlag.RestrictedMemberAccess (RMA) 标志的 ReflectionPermission 对象添加到授予集中。
例如,宿主可能会向 Internet 应用程序授予 Internet 权限及 RMA,以使 Internet 应用程序可以发出访问其自己的程序集中的私有数据的代码。 由于访问限于具有相同或更低信任的程序集,因此 Internet 应用程序无法访问 .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 方法在目标应用程序域中创建该对象并返回可用来调用该对象的属性和方法的代理。
注意 如果您在 Visual Studio 中使用该代码,则必须更改类的名称以包括命名空间。默认情况下,命名空间是项目的名称。例如,如果项目为“PartialTrust”,则类名必须为“PartialTrust.Worker”。
添加代码以调用 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, 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 可见性检查时,必须向它们授予完全信任。
示例
说明
下面的代码示例演示 RestrictedMemberAccess 标志的用法,以允许匿名承载的动态方法跳过 JIT 可见性检查,前提是目标成员的信任级别等于或低于发出此代码的程序集。
该示例定义了一个 Worker 类,该类可以跨应用程序域边界进行封送。 该类有两个发出并执行动态方法的 AccessPrivateMethod 方法重载。 第一个重载发出调用 Worker 类的私有 PrivateMethod 方法的动态方法,它可以发出跳过或不跳过 JIT 可见性检查的动态方法。 第二个重载发出访问 String 类的 internal 属性(在 Visual Basic 中为 Friend 属性)的动态方法。
该示例使用帮助器方法来创建一个限于 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”。