Walkthrough: Emitting Code in Partial Trust Scenarios

Reflection emit uses the same API set in full or partial trust, but some features require special permissions in partially trusted code. In addition, reflection emit has a feature, anonymously hosted dynamic methods, that is designed to be used with partial trust and by security-transparent assemblies.

Note

Before .NET Framework version 3.5, emitting code required ReflectionPermission with the ReflectionPermissionFlag.ReflectionEmit flag. This permission is included by default in the FullTrust and Intranet named permission sets, but not in the Internet permission set. Therefore, a library could be used from partial trust only if it had the SecurityCriticalAttribute attribute and also executed an Assert method for ReflectionEmit. Such libraries require careful security review because coding errors could result in security holes. The .NET Framework 3.5 allows code to be emitted in partial trust scenarios without issuing any security demands, because generating code is not inherently a privileged operation. That is, the generated code has no more permissions than the assembly that emits it. This enables libraries that emit code to be security-transparent and removes the need to assert ReflectionEmit, so that writing a secure library does not require such a thorough security review. 

This walkthrough illustrates the following tasks:

  • Setting up partially trusted environments for testing code.

  • Running code in partially trusted application domains.

  • Using anonymously hosted dynamic methods to emit and execute code in partial trust.

For more information about emitting code in partial trust scenarios, see Security Issues in Reflection Emit.

For a complete listing of the code shown in these procedures, see the Example section at the end of this walkthrough.

Setting up Partially Trusted Locations

The following procedures show how to set up locations from which code can be run with partial trust.

  • The first procedure shows how to create a sandboxed application domain in which code executes with Internet trust. It also explains a common pitfall.

  • The second procedure shows how to add ReflectionPermission with the ReflectionPermissionFlag.RestrictedMemberAccess flag to the partially trusted application domain, to enable access to private data in assemblies of equal or lesser trust.

  • The third procedure shows how to create a code group that associates a trust level with a folder, so that any assemblies located in that folder execute with partial trust.

In addition to these procedures, in simple scenarios you can use an assembly attribute such as the following to execute an assembly without SkipVerification permission. In a similar manner, you can use an attribute to refuse MemberAccess permission.

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

Creating Sandboxed Application Domains

To create an application domain in which your assemblies run with partial trust, you must specify the set of permissions to be granted to the assemblies by using the AppDomain.CreateDomain(String, Evidence, AppDomainSetup, PermissionSet, array<StrongName[]) method overload to create the application domain. The easiest way to specify the grant set is to retrieve a named permission set from security policy.

Warning

You cannot create a sandboxed application domain by specifying only evidence. You must specify a grant set or an application domain policy level. (Setting an application domain policy level is not discussed in this topic.) For example, if you use the CreateDomain(String, Evidence) method overload with Internet evidence, the permissions are enforced only at the application domain boundary. Within the application domain, assemblies are granted permissions based on standard security policy. For a console application on your computer, that would be full trust.

The following procedure creates a sandboxed application domain that runs your code with partial trust, to test scenarios in which emitted code can access only public members of public types. A subsequent procedure shows how to add RestrictedMemberAccess, to test scenarios in which emitted code can access nonpublic types and members in assemblies that are granted equal or lesser permissions.

To create an application domain with partial trust

  1. Use the following helper function to obtain named permission sets from security policy.

    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;
    }
    

    A grant set is the intersection of the permission sets granted at all policy levels. That is, a particular permission is not granted unless it is granted at all policy levels. Therefore, the helper function starts with a grant set for full trust and enumerates the levels of the policy hierarchy, taking the intersection of that grant set with the permission set defined for each level.

    Note

    You can create grant sets that contain any combination of permissions by using the PermissionSet class.

    The use of the helper function is demonstrated later in this procedure.

  2. Create evidence for the partially trusted location by using security zones. In this case, the Internet zone is used.

    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. Create an AppDomainSetup object to initialize the application domain with an application path. This code example uses the current folder.

    Dim adSetup As New AppDomainSetup()
    adSetup.ApplicationBase = "."
    
    AppDomainSetup adSetup = new AppDomainSetup();
    adSetup.ApplicationBase = ".";
    
  4. Use the helper function to retrieve the named permission set from system policy.

    Dim internetSet As PermissionSet = GetNamedPermissionSet("Internet")
    
    PermissionSet internetSet = GetNamedPermissionSet("Internet");
    
  5. Create the application domain, specifying the evidence, the application domain setup information, and the grant set.

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

    The last parameter of the AppDomain.CreateDomain(String, Evidence, AppDomainSetup, PermissionSet, array<StrongName[]) method overload enables you to specify a set of assemblies that are to be granted full trust, instead of the grant set of the application domain. You do not have to specify the .NET Framework assemblies that your application uses, because those assemblies are in the global assembly cache. Assemblies in the global assembly cache are always fully trusted. You can use this parameter to specify strong-named assemblies that are not in the global assembly cache. 

Adding RestrictedMemberAccess to Sandboxed Domains

Host applications can allow anonymously hosted dynamic methods to have access to private data in assemblies that have trust levels equal to or less than the trust level of the assembly that emits the code. To enable this restricted ability to skip just-in-time (JIT) visibility checks, the host application adds a ReflectionPermission object with the ReflectionPermissionFlag.RestrictedMemberAccess (RMA) flag to the grant set.

For example, a host might grant Internet applications Internet permissions plus RMA, so that an Internet application can emit code that accesses private data in its own assemblies. Because the access is limited to assemblies of equal or lesser trust, an Internet application cannot access members of fully trusted assemblies such as .NET Framework assemblies.

Note

To prevent elevation of privilege, stack information for the emitting assembly is included when anonymously hosted dynamic methods are constructed. When the method is invoked, the stack information is checked. Thus, an anonymously hosted dynamic method that is invoked from fully trusted code is still limited to the trust level of the emitting assembly.

To create an application domain with partial trust plus RMA

  1. Use the helper function to retrieve the Internet named permission set from security policy.

    internetSet = GetNamedPermissionSet("Internet")
    
    internetSet = GetNamedPermissionSet("Internet");
    
  2. Create a new ReflectionPermission object with the RestrictedMemberAccess flag, and use the PermissionSet.SetPermission method to add the permission to the grant set.

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

    The AddPermission method adds the permission to the grant set if it is not already included. If the permission is already included in the grant set, the specified flags are added to the existing permission.

    Note

    RMA is a feature of anonymously hosted dynamic methods. When ordinary dynamic methods skip JIT visibility checks, the emitted code must be granted ReflectionPermission with the ReflectionPermissionFlag.MemberAccess flag in addition to the RestrictedMemberAccess flag.

  3. Create the application domain, specifying the evidence, the application domain setup information, and the grant set.

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

Creating a Folder with Restricted Permissions

The following procedure shows how to create a code group that associates Internet permissions with a folder, and how to add the RestrictedMemberAccess flag to the grant set for code that is run from the folder.

To create a folder that has Internet permissions

  1. Click Start, point to Control Panel, point to Administrative Tools, and then click Microsoft .NET Framework 3.5 Configuration. You must have system administrator privileges to use the configuration tool.

  2. In the left pane, under .NET Framework 2.0 Configuration, expand My Computer, Runtime Security Policy, Machine, Code Groups, All_Code.

  3. In the right pane, click Add a Child Code Group to run the Create Code Group Wizard.

  4. Give the code group a name, such as "Internet Sandbox", and optionally a description. Click Next.

  5. In the Choose the condition type for this code group list, select URL. In the URL box, type the full path of the folder you want to use, and then click Next. For example, you might type the following:

    file://c:/InternetSandbox/*
    
  6. Select Internet from the Use existing permission set list, and then click Next.

    Note

    To specify a named permission set and a ReflectionPermission object with the RestrictedMemberAccess flag, click Create a new permission set and specify the custom permission set by using an XML file.

  7. Click Finish to create the code group.

  8. Place the assemblies that you want to run with limited trust in the folder that you specified in step 5.

Running Code in Sandboxed Application Domains

The following procedure explains how to define a class by using methods that can be executed in an application domain, how to create an instance of the class in the domain, and how to execute its methods.

To define and execute a method in an application domain

  1. Define a class that derives from MarshalByRefObject. This enables you to create instances of the class in other application domains and to make method calls across application domain boundaries. The class in this example is named Worker.

    Public Class Worker
        Inherits MarshalByRefObject
    
    public class Worker : MarshalByRefObject
    {
    
  2. Define a public method that contains the code you want to execute. In this example, the code emits a simple dynamic method, creates a delegate to execute the method, and invokes the delegate.

    Public 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. In your main program, get the display name of your assembly. This name is used when you create instances of the Worker class in the sandboxed application domain.

    Dim asmName As String = [Assembly].GetExecutingAssembly().FullName
    
    String asmName = Assembly.GetExecutingAssembly().FullName;
    
  4. In your main program, create a sandboxed application domain, as described in the first procedure in this walkthrough. You do not have to add any permissions to the Internet permission set, because the SimpleEmitDemo method uses only public methods.

  5. In your main program, create an instance of the Worker class in the sandboxed application domain.

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

    The CreateInstanceAndUnwrap method creates the object in the target application domain and returns a proxy that can be used to call the properties and methods of the object.

    Note

    If you use this code in Visual Studio, you must change the name of the class to include the namespace. By default, the namespace is the name of the project. For example, if the project is "PartialTrust", the class name must be "PartialTrust.Worker".

  6. Add code to call the SimpleEmitDemo method. The call is marshaled across the application domain boundary, and the code is executed in the sandboxed application domain.

    w.SimpleEmitDemo()
    
    w.SimpleEmitDemo();
    

Using Anonymously Hosted Dynamic Methods

Anonymously hosted dynamic methods are associated with an assembly that is provided by the system. Therefore, they are isolated from other code. Ordinary dynamic methods, on the other hand, must be associated with an existing module or type.

Note

The only way to associate a dynamic method with the assembly that provides anonymous hosting is to use the constructors that are described in the following procedure. You cannot explicitly specify a module in the anonymous hosting assembly.

Ordinary dynamic methods have access to the internal members of the module they are associated with, or to the private members of the type they are associated with. Because anonymously hosted dynamic methods are isolated from other code, they do not have access to private data. However, they do have a restricted ability to skip JIT visibility checks to gain access to private data. This ability is limited to assemblies that have trust levels equal to or less than the trust level of the assembly that emits the code.

To prevent elevation of privilege, stack information for the emitting assembly is included when anonymously hosted dynamic methods are constructed. When the method is invoked, the stack information is checked. An anonymously hosted dynamic method that is invoked from fully trusted code is still limited to the trust level of the assembly that emitted it.

To use anonymously hosted dynamic methods

  • Create an anonymously hosted dynamic method by using a constructor that does not specify an associated module or type.

    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);
    

    If an anonymously hosted dynamic method uses only public types and methods, it does not require restricted member access and does not have to skip JIT visibility checks.

    No special permissions are required to emit a dynamic method, but the emitted code requires the permissions that are demanded by the types and methods it uses. For example, if the emitted code calls a method that accesses a file, it requires FileIOPermission. If the trust level does not include that permission, a security exception is thrown when the emitted code is executed. The code shown here emits a dynamic method that uses only the Console.WriteLine method. Therefore, the code can be executed from partially trusted locations.

  • Alternatively, create an anonymously hosted dynamic method with restricted ability to skip JIT visibility checks, by using the DynamicMethod(String, Type, array<Type[], Boolean) constructor and specifying true for the restrictedSkipVisibility parameter.

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

    The restriction is that the anonymously hosted dynamic method can access private data only in assemblies with trust levels equal to or less than the trust level of the emitting assembly. For example, if the dynamic method is executing with Internet trust, it can access private data in other assemblies that are also executing with Internet trust, but it cannot access private data of .NET Framework assemblies. .NET Framework assemblies are installed in the global assembly cache and are always fully trusted.

    Anonymously hosted dynamic methods can use this restricted ability to skip JIT visibility checks only if the host application grants ReflectionPermission with the ReflectionPermissionFlag.RestrictedMemberAccess flag. The demand for this permission is made when the method is invoked.

    Note

    Call stack information for the emitting assembly is included when the dynamic method is constructed. Therefore, the demand is made against the permissions of the emitting assembly instead of the assembly that invokes the method. This prevents the emitted code from being executed with elevated permissions.

    The complete code example at the end of this walkthrough demonstrates the use and limitations of restricted member access. Its Worker class includes a method that can create anonymously hosted dynamic methods with or without the restricted ability to skip visibility checks, and the example shows the result of executing this method in application domains that have different trust levels.

    Note

    The restricted ability to skip visibility checks is a feature of anonymously hosted dynamic methods. When ordinary dynamic methods skip JIT visibility checks, they must be granted ReflectionPermission with the ReflectionPermissionFlag.MemberAccess flag. In addition, ordinary dynamic methods that access private data in assemblies other than the emitting assembly must have either ReflectionPermission with the RestrictedMemberAccess flag or SecurityPermission with the SecurityPermissionFlag.ControlEvidence flag.

Example

Description

The following code example demonstrates the use of the RestrictedMemberAccess flag to allow anonymously hosted dynamic methods to skip JIT visibility checks, but only when the target member is at an equal or lower level of trust than the assembly that emits the code.

The example defines a Worker class that can be marshaled across application domain boundaries. The class has two AccessPrivateMethod method overloads that emit and execute dynamic methods. The first overload emits a dynamic method that calls the private PrivateMethod method of the Worker class, and it can emit the dynamic method with or without JIT visibility checks. The second overload emits a dynamic method that accesses an internal property (Friend property in Visual Basic) of the String class.

The example uses a helper method to get the Internet permission set from security policy, and then creates an application domain, using the AppDomain.CreateDomain(String, Evidence, AppDomainSetup, PermissionSet, array<StrongName[]) method overload to specify that all code that executes in the domain uses this grant set. The example creates an instance of the Worker class in the application domain, and executes the AccessPrivateMethod method two times.

  • The first time the AccessPrivateMethod method is executed, JIT visibility checks are enforced. The dynamic method fails when it is invoked, because JIT visibility checks prevent it from accessing the private method.

  • The second time the AccessPrivateMethod method is executed, JIT visibility checks are skipped. The dynamic method fails when it is compiled, because the Internet grant set does not grant sufficient permissions to skip visibility checks.

The example uses a helper method to get the Internet grant set, and adds ReflectionPermission with ReflectionPermissionFlag.RestrictedMemberAccess to the grant set. The example then creates a second domain, specifying that all code that executes in the domain is granted the permissions in the new grant set. The example creates an instance of the Worker class in the new application domain, and executes both overloads of the AccessPrivateMethod method.

  • The first overload of the AccessPrivateMethod method is executed, and JIT visibility checks are skipped. The dynamic method compiles and executes successfully, because the assembly that emits the code is the same as the assembly that contains the private method. Therefore, the trust levels are equal. If the application that contains the Worker class had several assemblies, the same process would succeed for any one of those assemblies, because they would all be at the same trust level.

  • The second overload of the AccessPrivateMethod method is executed, and again JIT visibility checks are skipped. This time the dynamic method fails when it is compiled, because it tries to access the internal FirstChar property of the String class. The assembly that contains the String class is fully trusted. Therefore, it is at a higher level of trust than the assembly that emits the code.

This comparison shows how ReflectionPermissionFlag.RestrictedMemberAccess enables partially trusted code to skip visibility checks for other partially trusted code without compromising the security of trusted code.

Code

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.
 */

Compiling the Code

  • If you build this code example in Visual Studio, you must change the name of the class to include the namespace when you pass it to the CreateInstanceAndUnwrap method. By default, the namespace is the name of the project. For example, if the project is "PartialTrust", the class name must be "PartialTrust.Worker".

See Also

Tasks

How to: Run Partially Trusted Code in a Sandbox

Concepts

Security Issues in Reflection Emit