Tutorial: Emisión de código en escenarios que no son de plena confianza

La emisión de reflexión usa el mismo conjunto de API con confianza completa o parcial, pero algunas características requieren permisos especiales en entornos de confianza parcial. Además, la emisión de reflexión tiene una característica, los métodos dinámicos hospedados de forma anónima, diseñada para su uso en entornos de confianza parcial y por ensamblados transparentes en seguridad.

Nota

Antes de .NET Framework 3.5, la emisión de código requería ReflectionPermission con la marca ReflectionPermissionFlag.ReflectionEmit. Este permiso está incluido de forma predeterminada en los conjuntos de permisos con nombre FullTrust e Intranet, pero no en el conjunto de permisos Internet. Por tanto, se podría usar una biblioteca de confianza parcial solo si tuviera el atributo SecurityCriticalAttribute y también se ejecutara un método Assert para ReflectionEmit. Estas bibliotecas requieren una revisión cuidadosa de la seguridad porque los errores de codificación pueden provocar vulnerabilidades de seguridad. .NET Framework 3.5 permite emitir código en escenarios de confianza parcial sin emitir ninguna petición de seguridad, porque la generación de código no es en sí una operación que requiera privilegios. Es decir, el código generado no tiene más permisos que el ensamblado que lo emite. Esto permite que las bibliotecas que emiten código sean transparentes en seguridad y quita la necesidad de validar ReflectionEmit, de manera que escribir una biblioteca segura no requiere este tipo de revisión exhaustiva de la seguridad.

En este tutorial se muestran las tareas siguientes:

Para obtener más información sobre cómo emitir código en escenarios de confianza parcial, vea Problemas de seguridad en la emisión de la reflexión.

Para obtener una lista completa del código mostrado en estos procedimientos, vea la sección Ejemplo al final de este tutorial.

Preparar las ubicaciones de confianza parcial

En los dos procedimientos siguientes se muestra cómo preparar las ubicaciones desde las que puede probar el código con confianza parcial.

  • En el primer procedimiento se muestra cómo crear un dominio de aplicación en un espacio aislado en el que el código tiene permisos de Internet.

  • En el segundo procedimiento se muestra cómo agregar ReflectionPermission con la marca ReflectionPermissionFlag.RestrictedMemberAccess a un dominio de aplicación de confianza parcial, y de este modo habilitar a los ensamblados con una confianza igual o menor para el acceso a los datos privados.

Crear dominios de aplicación en recintos de seguridad

Para crear un dominio de aplicación en el que los ensamblados se ejecuten con confianza parcial, debe especificar el conjunto de permisos que se va a conceder a los ensamblados mediante la sobrecarga del método AppDomain.CreateDomain(String, Evidence, AppDomainSetup, PermissionSet, StrongName[]) para crear el dominio de aplicación. La manera más fácil de especificar el conjunto de permisos concedidos es recuperar un conjunto de permisos con nombre de la directiva de seguridad.

En el procedimiento siguiente se crea un dominio de aplicación en un espacio aislado que ejecuta el código de confianza parcial, para probar los escenarios en los que el código emitido sólo puede tener acceso a los miembros públicos de tipos públicos. En un procedimiento posterior se muestra cómo agregar RestrictedMemberAccess para probar los escenarios en los que el código emitido puede tener acceso a los tipos y miembros no públicos de los ensamblados con permisos iguales o menores.

Para crear un dominio de aplicación con confianza parcial

  1. Cree un conjunto de permisos que se concedan a los ensamblados del dominio de aplicación en un espacio aislado. En este caso, se utiliza el conjunto de permisos de la zona de Internet.

    Evidence ev = new Evidence();
    ev.AddHostEvidence(new Zone(SecurityZone.Internet));
    PermissionSet pset = new NamedPermissionSet("Internet", SecurityManager.GetStandardSandbox(ev));
    
    Dim ev As New Evidence()
    ev.AddHostEvidence(new Zone(SecurityZone.Internet))
    Dim pset As New NamedPermissionSet("Internet", SecurityManager.GetStandardSandbox(ev))
    
  2. Cree un objeto AppDomainSetup para inicializar el dominio de aplicación con una ruta de aplicación.

    Importante

    Para simplificar, en este ejemplo de código se usa la carpeta actual. Para ejecutar código que procede de Internet, use una carpeta independiente para el código que no es de confianza, como se describe en Cómo: Ejecutar código de confianza parcial en un espacio aislado.

    AppDomainSetup adSetup = new AppDomainSetup();
    adSetup.ApplicationBase = ".";
    
    Dim adSetup As New AppDomainSetup()
    adSetup.ApplicationBase = "."
    
  3. Cree el dominio de aplicación, especificando la información sobre configuración del dominio de aplicación y el conjunto de permisos concedidos para todos los ensamblados que se ejecutan en el dominio de aplicación.

    AppDomain ad = AppDomain.CreateDomain("Sandbox", ev, adSetup, pset, null);
    
    Dim ad As AppDomain = AppDomain.CreateDomain("Sandbox", ev, adSetup, pset, Nothing)
    

    El último parámetro de la sobrecarga del método AppDomain.CreateDomain(String, Evidence, AppDomainSetup, PermissionSet, StrongName[]) permite especificar un conjunto de ensamblados a los que se concederá plena confianza, en lugar del conjunto de permisos concedidos del dominio de aplicación. No es necesario especificar los ensamblados de .NET Framework que la aplicación usa, porque esos ensamblados están en la caché global de ensamblados. Los ensamblados que están en la caché global de ensamblados siempre son de plena confianza. Puede usar este parámetro para especificar ensamblados con nombre seguro que no están en la caché global de ensamblados.

Agregar RestrictedMemberAccess a dominios en recintos de seguridad

Las aplicaciones host pueden permitir que los métodos dinámicos hospedados de forma anónima tengan acceso a los datos privados de ensamblados con niveles de confianza igual o menor que el nivel de confianza del ensamblado que emite el código. Para habilitar esta capacidad restringida para omitir las comprobaciones de visibilidad Just-In-Time (JIT), la aplicación host agrega un objeto ReflectionPermission con el marcador ReflectionPermissionFlag.RestrictedMemberAccess (RMA) al conjunto de permisos concedidos.

Por ejemplo, un host podría conceder a aplicaciones de Internet permisos de Internet más RMA, de manera que una aplicación de Internet pueda emitir código que tiene acceso a los datos privados de sus propios ensamblados. Debido a que el acceso se limita a los ensamblados con una confianza igual o menor, una aplicación de Internet no puede acceder a los miembros de ensamblados de plena confianza, como los ensamblados de .NET Framework.

Nota

Para evitar la elevación de privilegios, la información de pila del ensamblado emisor se incluye al construir los métodos dinámicos hospedados de forma anónima. Cuando se invoca el método, se comprueba la información de la pila. Así, un método dinámico hospedado de forma anónima que se invoca desde código de plena confianza seguirá limitado al nivel de confianza del ensamblado emisor.

Para crear un dominio de aplicación con confianza parcial más RMA

  1. Cree un nuevo objeto ReflectionPermission con la marca (RMA) RestrictedMemberAccess y use el método PermissionSet.SetPermission para agregar el permiso al conjunto de permisos concedidos.

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

    El método AddPermission agrega el permiso al conjunto de permisos concedidos si aún no está incluido. Si el permiso ya está incluido en el conjunto de permisos concedidos, los marcadores especificados se agregan al permiso existente.

    Nota

    RMA es una característica de los métodos dinámicos hospedados de forma anónima. Cuando los métodos dinámicos normales omiten las comprobaciones de visibilidad JIT, el código emitido requiere plena confianza.

  2. Cree el dominio de aplicación y especifique la información sobre configuración del dominio de aplicación y el conjunto de permisos concedidos.

    ad = AppDomain.CreateDomain("Sandbox2", ev, adSetup, pset, null);
    
    ad = AppDomain.CreateDomain("Sandbox2", ev, adSetup, pset, Nothing)
    

Ejecutar código en dominios de aplicación en un espacio aislado

En el procedimiento siguiente se explica cómo definir una clase mediante métodos que se pueden ejecutar en un dominio de aplicación, cómo crear una instancia de la clase en el dominio y cómo ejecutar sus métodos.

Para definir y ejecutar un método en un dominio de aplicación

  1. Defina una clase que se derive de MarshalByRefObject. Esto permite crear instancias de la clase en otros dominios de aplicación y realizar llamadas a métodos en los límites del dominio de aplicación. En este ejemplo, la clase se denomina Worker.

    public class Worker : MarshalByRefObject
    {
    
    Public Class Worker
        Inherits MarshalByRefObject
    
  2. Defina un método público que contenga el código que desea ejecutar. En este ejemplo, el código emite un método dinámico simple, crea un delegado para ejecutar el método e invoca al delegado.

    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();
    }
    
    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
    
  3. En el programa principal, obtenga el nombre para mostrar del ensamblado. Este nombre se usa al crear instancias de la clase Worker en el dominio de aplicación en un espacio aislado.

    String asmName = typeof(Worker).Assembly.FullName;
    
    Dim asmName As String = GetType(Worker).Assembly.FullName
    
  4. En el programa principal, cree un dominio de aplicación en un espacio aislado, como se describe en el primer procedimiento de este tutorial. No es necesario agregar ningún permiso al conjunto de permisos Internet, porque el método SimpleEmitDemo sólo usa métodos públicos.

  5. En el programa principal, cree una instancia de la clase Worker en el dominio de aplicación en un espacio aislado.

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

    El método CreateInstanceAndUnwrap crea el objeto en el dominio de aplicación de destino y devuelve un proxy que se puede usar para llamar a las propiedades y métodos del objeto.

    Nota

    Si usa este código en Visual Studio, debe cambiar el nombre de la clase para que incluya el espacio de nombres. De forma predeterminada, el espacio de nombres es el nombre del proyecto. Por ejemplo, si el proyecto es "PartialTrust", el nombre de clase debe ser "PartialTrust.Worker".

  6. Agregue código para llamar al método SimpleEmitDemo. La llamada se serializa a través del límite del dominio de aplicación y el código se ejecuta en el dominio de aplicación en espacio aislado.

    w.SimpleEmitDemo();
    
    w.SimpleEmitDemo()
    

Usar métodos dinámicos hospedados de forma anónima

Los métodos dinámicos hospedados de forma anónima están asociados a un ensamblado transparente proporcionado por el sistema. Por consiguiente, el código que contienen es transparente. Los métodos dinámicos normales, por otro lado, deben estar asociados a un módulo existente (directamente especificados o deducidos de un tipo asociado) y toman el nivel de seguridad de ese módulo.

Nota

La única manera de asociar un método dinámico al ensamblado que proporciona hospedaje anónimo es usar los constructores que se describen en el procedimiento siguiente. No puede especificar explícitamente un módulo en el ensamblado hospedado de forma anónima.

Los métodos dinámicos normales tienen acceso a los miembros internos del módulo al que están asociados o a los miembros privados del tipo al que están asociados. Debido a que los métodos dinámicos de hospedaje anónimo están aislados del resto del código, no tienen acceso a los datos privados. Sin embargo, tienen una capacidad restringida para omitir las comprobaciones de visibilidad JIT para obtener acceso a los datos privados. Esta capacidad se limita a los ensamblados que tienen un nivel de confianza igual o menor que el nivel de confianza del ensamblado que emite el código.

Para evitar la elevación de privilegios, la información de pila del ensamblado emisor se incluye al construir los métodos dinámicos hospedados de forma anónima. Cuando se invoca el método, se comprueba la información de la pila. Un método dinámico hospedado de forma anónima que se invoca desde código de plena confianza seguirá limitado al nivel de confianza del ensamblado que lo emitió.

Para usar métodos dinámicos hospedados de forma anónima

  • Cree un método dinámico hospedado de forma anónima mediante un constructor que no especifique ningún módulo o tipo asociado.

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

    Si un método dinámico hospedado de forma anónima sólo usa tipos y métodos públicos, no necesita acceso restringido a los miembros y no tiene que omitir las comprobaciones de visibilidad JIT.

    No son necesarios permisos especiales para emitir un método dinámico, pero el código emitido requiere los permisos exigidos por los tipos y métodos que use. Por ejemplo, si el código emitido llama a un método que tiene acceso a un archivo, requiere FileIOPermission. Si el nivel de confianza no incluye ese permiso, se produce una excepción de seguridad al ejecutar el código emitido. El código mostrado aquí emite un método dinámico que sólo usa el método Console.WriteLine. Por consiguiente, el código se puede ejecutar desde ubicaciones de confianza parcial.

  • Opcionalmente, para crear un método dinámico de host anónimo con capacidad restringida para omitir las comprobaciones de visibilidad JIT, puede usar el constructor DynamicMethod(String, Type, Type[], Boolean) y especificar true para el parámetro restrictedSkipVisibility.

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

    La restricción es que el método dinámico hospedado de forma anónima sólo puede tener acceso a los datos privados de ensamblados con niveles de confianza iguales o menores que el nivel de confianza del ensamblado emisor. Por ejemplo, si el método dinámico se ejecuta con confianza en Internet, puede acceder a los datos privados de otros ensamblados que también se ejecutan con confianza en Internet, pero no puede acceder a los datos privados de ensamblados de .NET Framework. Los ensamblados de .NET Framework están instalados en la caché global de ensamblados y son siempre de plena confianza.

    Los métodos dinámicos hospedados de forma anónima pueden usar esta capacidad restringida para omitir las comprobaciones de visibilidad JIT sólo si la aplicación host concede ReflectionPermission con el marcador ReflectionPermissionFlag.RestrictedMemberAccess. La petición de este permiso se realiza al invocar el método.

    Nota

    La información de pila de llamadas del ensamblado emisor se incluye al construir el método dinámico. Por consiguiente, se realiza una petición de los permisos del ensamblado emisor, no del ensamblado que invoca el método. Así se evita que el código emitido se ejecute con permisos elevados.

    El ejemplo de código completo al final de este tutorial muestra el uso y las limitaciones de acceso a miembros restringidos. Su clase Worker incluye un método que puede crear métodos dinámicos hospedados de forma anónima con o sin la capacidad restringida de omitir las comprobaciones de visibilidad y el ejemplo muestra el resultado de ejecutar este método en dominios de aplicación que tienen niveles de confianza diferentes.

    Nota

    La capacidad restringida de omitir las comprobaciones de visibilidad es una característica de los métodos dinámicos hospedados de forma anónima. Cuando los métodos dinámicos normales omiten las comprobaciones de visibilidad JIT, se les debe otorgar plena confianza.

Ejemplo

Descripción

En el ejemplo de código siguiente se muestra cómo se utiliza el marcador RestrictedMemberAccess para permitir que los métodos dinámicos hospedados de forma anónima omitan las comprobaciones de visibilidad JIT, pero sólo cuando el miembro de destino tiene un nivel de confianza igual o menor que el ensamblado que emite el código.

En el ejemplo se define una clase Worker que se puede serializar en los límites del dominio de aplicación. La clase tiene dos sobrecargas de método AccessPrivateMethod que emiten y ejecutan métodos dinámicos. La primera sobrecarga emite un método dinámico que llama al método privado PrivateMethod de la clase Worker y puede emitir el método dinámico con o sin comprobaciones de visibilidad JIT. La segunda sobrecarga emite un método dinámico que tiene acceso a una propiedad internal (propiedad Friend en Visual Basic) de la clase String.

El ejemplo usa un método del asistente para crear un conjunto de permisos concedidos limitado a los permisos de Internet y crea un dominio de aplicación mediante la sobrecarga del método AppDomain.CreateDomain(String, Evidence, AppDomainSetup, PermissionSet, StrongName[]) para especificar que todo el código que se ejecute en el dominio usa este conjunto de permisos concedidos. En el ejemplo se crea una instancia de la clase Worker en el dominio de aplicación y se ejecuta el método AccessPrivateMethod dos veces.

  • La primera vez que se ejecuta el método AccessPrivateMethod, se exigen las comprobaciones de visibilidad JIT. Se producirá un error en el método dinámico cuando se invoque, porque las comprobaciones de visibilidad JIT le impedirán el acceso al método privado.

  • La segunda vez que se ejecuta el método AccessPrivateMethod, se omiten las comprobaciones de visibilidad JIT. Se producirá un error en el método dinámico al compilarse, porque el conjunto de permisos concedidos Internet no concede permisos suficientes para omitir las comprobaciones de visibilidad.

El ejemplo siguiente agrega ReflectionPermission con ReflectionPermissionFlag.RestrictedMemberAccess al conjunto de permisos concedidos. A continuación, en el ejemplo se crea un segundo dominio y se especifica que todo el código que se ejecute en el dominio tenga los permisos del nuevo conjunto de permisos concedidos. En el ejemplo se crea una instancia de la clase Worker en el nuevo dominio de aplicación y se ejecutan las dos sobrecargas del método AccessPrivateMethod.

  • Se ejecuta la primera sobrecarga del método AccessPrivateMethod y se omiten las comprobaciones de visibilidad JIT. El método dinámico se compila y se ejecuta correctamente, porque el ensamblado que emite el código es igual que el ensamblado que contiene el método privado. Por consiguiente, los niveles de confianza son iguales. Si la aplicación que contiene la clase Worker tuviera varios ensamblados, el mismo proceso sería correcto para cualquiera de esos ensamblados, porque todos tendrían el mismo nivel de confianza.

  • Se ejecuta la segunda sobrecarga del método AccessPrivateMethod y, de nuevo, se omiten las comprobaciones de visibilidad JIT. Esta vez, se produce un error en el método dinámico al compilarse porque intenta tener acceso a la propiedad internalFirstChar de la clase String. El ensamblado que contiene la clase String es de plena confianza. Por consiguiente, está en un nivel de confianza superior al del ensamblado que emite el código.

Esta comparación muestra cómo ReflectionPermissionFlag.RestrictedMemberAccess habilita al código de confianza parcial para omitir las comprobaciones de visibilidad de otro código de confianza parcial sin poner en peligro la seguridad del código de confianza.

Código

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 = typeof(Worker).Assembly.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.
 */
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 = GetType(Worker).Assembly.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.
' 

Compilar el código

  • Si compila este código de ejemplo en Visual Studio, debe cambiar el nombre de la clase para que incluya el espacio de nombres al pasarla al método CreateInstanceAndUnwrap. De forma predeterminada, el espacio de nombres es el nombre del proyecto. Por ejemplo, si el proyecto es "PartialTrust", el nombre de clase debe ser "PartialTrust.Worker".

Vea también