Genomgång: Generera kod i partiella förtroendescenarier

Reflection emit använder samma API-uppsättning i fullständigt eller partiellt förtroende, men vissa funktioner kräver särskilda behörigheter i delvis betrodd kod. Dessutom har reflection emit en funktion, anonymt värdbaserade dynamiska metoder, som är utformad för att användas med partiellt förtroende och av säkerhetstransparent sammansättningar.

Anmärkning

Innan .NET Framework 3.5 krävdes det att generera kod med flaggan ReflectionPermission tillsammans med ReflectionPermissionFlag.ReflectionEmit. Den här behörigheten ingår som standard i FullTrust och Intranet namngivna behörighetsuppsättningar, men inte i Internet behörighetsuppsättning. Därför kan ett bibliotek endast användas från partiellt förtroende om det hade attributet SecurityCriticalAttribute och även körde en Assert-metod för ReflectionEmit. Sådana bibliotek kräver noggrann säkerhetsgranskning eftersom kodfel kan leda till säkerhetshål. .NET Framework 3.5 tillåter att kod genereras i partiella förtroendescenarier utan att utfärda några säkerhetskrav, eftersom det inte är en privilegierad åtgärd att generera kod. Den genererade koden har alltså inte fler behörigheter än den sammansättning som genererar den. Detta gör det möjligt för bibliotek som genererar kod att vara säkerhetstransparenta och tar bort behovet av att kontrollera ReflectionEmit, så att det inte krävs någon sådan grundlig säkerhetsgranskning för att skriva ett säkert bibliotek.

I den här genomgången tas följande aktiviteter upp:

Mer information om hur du genererar kod i partiella förtroendescenarier finns i säkerhetsproblem i Reflection Emit.

En fullständig lista över koden som visas i dessa procedurer finns i avsnittet Exempel i slutet av den här genomgången.

Konfigurera delvis betrodda platser

Följande två procedurer visar hur du konfigurerar platser där du kan testa kod med partiellt förtroende.

  • Den första proceduren visar hur du skapar en sandbox-programdomän där kod beviljas Internetbehörigheter.

  • Den andra proceduren visar hur du lägger till ReflectionPermission med flaggan ReflectionPermissionFlag.RestrictedMemberAccess till en delvis betrodd programdomän för att ge åtkomst till privata data i sammansättningar med lika stort eller mindre förtroende.

Skapa sandbox-programdomäner

Om du vill skapa en programdomän där dina sammansättningar körs med partiellt förtroende måste du ange den uppsättning behörigheter som ska beviljas till sammansättningarna med hjälp av överlagringen av AppDomain.CreateDomain(String, Evidence, AppDomainSetup, PermissionSet, StrongName[])-metoden för att skapa programdomänen. Det enklaste sättet att ange tillståndsuppsättningen är att hämta en namngiven behörighetsuppsättning från säkerhetspolicyn.

Följande procedur skapar en sandboxad programdomän som kör koden med partiellt förtroende för att testa scenarier där emitterad kod endast kan komma åt offentliga medlemmar av offentliga typer. En efterföljande procedur visar hur du lägger till RestrictedMemberAccess, för att testa scenarier där genererad kod kan komma åt icke-offentliga typer och medlemmar i sammansättningar som beviljas lika med eller mindre behörigheter.

Skapa en programdomän med partiellt förtroende

  1. Skapa en behörighetsuppsättning som ska tilldelas till assembly:na i den begränsade applikationsdomänen. I det här fallet används behörighetsuppsättningen för Internet-zonen.

    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. Skapa ett AppDomainSetup objekt för att initiera programdomänen med en programsökväg.

    Viktigt!

    För enkelhetens skull använder det här kodexemplet den aktuella mappen. Om du vill köra kod som faktiskt kommer från Internet använder du en separat mapp för den ej betrodda koden enligt beskrivningen i How to: Run Partially Trusted Code in a Sandbox.

    AppDomainSetup adSetup = new AppDomainSetup();
    adSetup.ApplicationBase = ".";
    
    Dim adSetup As New AppDomainSetup()
    adSetup.ApplicationBase = "."
    
  3. Skapa programdomänen, ange konfigurationsinformation för programdomänen och beviljandeuppsättningen för alla sammansättningar som körs i programdomänen.

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

    Med den sista parametern i AppDomain.CreateDomain(String, Evidence, AppDomainSetup, PermissionSet, StrongName[]) metodöverlagring kan du ange en uppsättning sammansättningar som ska beviljas fullständigt förtroende i stället för programdomänens beviljandeuppsättning. Du behöver inte ange de .NET Framework-sammansättningar som ditt program använder, eftersom dessa sammansättningar finns i den globala sammansättningscachen. Sammansättningar i den globala sammansättningscachen är alltid fullt betrodda. Du kan använda den här parametern för att ange samlingar med starkt namn som inte finns i den globala assembly cachen.

Lägga till RestrictedMemberAccess till sandbox-domäner

Värdprogram kan tillåta anonymt värdbaserade dynamiska metoder att ha åtkomst till privata data i sammansättningar som har förtroendenivåer som är lika med eller mindre än förtroendenivån för sammansättningen som genererar koden. För att aktivera den här begränsade möjligheten att hoppa över synlighetskontroller just-in-time (JIT), lägger värdprogrammet till ett ReflectionPermission-objekt med flaggan ReflectionPermissionFlag.RestrictedMemberAccess (RMA) i beviljningsmängden.

En värd kan till exempel bevilja Internetprogram Internetbehörigheter plus RMA, så att ett Internetprogram kan generera kod som får åtkomst till privata data i sina egna sammansättningar. Eftersom åtkomsten är begränsad till sammansättningar med lika eller mindre förtroende kan ett Internetprogram inte komma åt medlemmar i fullständigt betrodda sammansättningar, till exempel .NET Framework-sammansättningar.

Anmärkning

För att förhindra utökade privilegier inkluderas stackinformation för den utsändande sammansättningen när anonymt värdbaserade dynamiska metoder konstrueras. När metoden anropas kontrolleras stackinformationen. Därför är en anonymt värdbaserad dynamisk metod som anropas från fullständigt betrodd kod fortfarande begränsad till förtroendenivån för den sändande sammansättningen.

Skapa en programdomän med partiellt förtroende plus RMA

  1. Skapa ett nytt ReflectionPermission-objekt med flaggan RestrictedMemberAccess (RMA) och använd metoden PermissionSet.SetPermission för att lägga till behörigheten i beviljandeuppsättningen.

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

    Metoden AddPermission lägger till behörigheten i uppsättningen för godkännanden om den inte redan ingår. Om behörigheten redan ingår i beviljandeuppsättningen läggs de angivna flaggorna till i den befintliga behörigheten.

    Anmärkning

    RMA är en funktion i anonymt värdbaserade dynamiska metoder. När vanliga dynamiska metoder hoppar över JIT-synlighetskontroller kräver den utgivna koden fullständigt förtroende.

  2. Skapa programdomänen, ange informationen för konfiguration av programdomänen och rättighetsuppsättningen.

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

Köra kod i begränsade programdomäner

Följande procedur beskriver hur du definierar en klass med hjälp av metoder som kan köras i en programdomän, hur du skapar en instans av klassen i domänen och hur du kör dess metoder.

Definiera och köra en metod i en programdomän

  1. Definiera en klass som härleds från MarshalByRefObject. På så sätt kan du skapa instanser av klassen i andra programdomäner och göra metodanrop över programdomängränser. Klassen i det här exemplet heter Worker.

    public class Worker : MarshalByRefObject
    {
    
    Public Class Worker
        Inherits MarshalByRefObject
    
  2. Definiera en offentlig metod som innehåller den kod som du vill köra. I det här exemplet genererar koden en enkel dynamisk metod, skapar ett ombud för att köra metoden och anropar ombudet.

    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. Hämta sammansättningens visningsnamn i huvudprogrammet. Det här namnet används när du skapar instanser av klassen Worker i den begränsade programdomänen.

    String asmName = typeof(Worker).Assembly.FullName;
    
    Dim asmName As String = GetType(Worker).Assembly.FullName
    
  4. I huvudprogrammet skapar du en sandbox-programdomän enligt beskrivningen i den första proceduren i den här genomgången. Du behöver inte lägga till några behörigheter i Internet behörighetsuppsättning, eftersom SimpleEmitDemo-metoden endast använder offentliga metoder.

  5. I huvudprogrammet skapar du en instans av klassen Worker i den begränsade programdomänen.

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

    Metoden CreateInstanceAndUnwrap skapar objektet i målprogramdomänen och returnerar en proxy som kan användas för att anropa objektets egenskaper och metoder.

    Anmärkning

    Om du använder den här koden i Visual Studio måste du ändra namnet på klassen så att den inkluderar namnområdet. Som standard är namnområdet namnet på projektet. Om projektet till exempel är "PartialTrust" måste klassnamnet vara "PartialTrust.Worker".

  6. Lägg till kod för att anropa metoden SimpleEmitDemo. Anropet hanteras över programdomängränsen och koden körs i den sandboxade programdomänen.

    w.SimpleEmitDemo();
    
    w.SimpleEmitDemo()
    

Använda anonymt värdbaserade dynamiska metoder

Anonymt värdbaserade dynamiska metoder är associerade med en transparent sammansättning som tillhandahålls av systemet. Därför är koden som de innehåller transparent. Vanliga dynamiska metoder måste å andra sidan associeras med en befintlig modul (oavsett om de är direkt angivna eller härledda från en associerad typ) och ta sin säkerhetsnivå från den modulen.

Anmärkning

Det enda sättet att associera en dynamisk metod med sammansättningen som tillhandahåller anonym värd är att använda konstruktorerna som beskrivs i följande procedur. Du kan inte uttryckligen ange en modul i den anonyma värdsammansättningen.

Vanliga dynamiska metoder har åtkomst till de interna medlemmarna i modulen som de är associerade med, eller till de privata medlemmarna av den typ som de är associerade med. Eftersom anonymt värdbaserade dynamiska metoder är isolerade från annan kod har de inte åtkomst till privata data. De har dock en begränsad möjlighet att hoppa över JIT-synlighetskontroller för att få åtkomst till privata data. Den här möjligheten är begränsad till sammansättningar som har förtroendenivåer som är lika med eller mindre än förtroendenivån för sammansättningen som genererar koden.

För att förhindra utökade privilegier inkluderas stackinformation för den utsändande sammansättningen när anonymt värdbaserade dynamiska metoder konstrueras. När metoden anropas kontrolleras stackinformationen. En anonymt värdbaserad dynamisk metod som anropas från fullständigt betrodd kod är fortfarande begränsad till förtroendenivån för den sammansättning som släppte ut den.

Använda anonymt värdbaserade dynamiska metoder

  • Skapa en anonymt värdbaserad dynamisk metod med hjälp av en konstruktor som inte anger någon associerad modul eller typ.

    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)
    

    Om en anonymt värdbaserad dynamisk metod endast använder offentliga typer och metoder kräver den inte begränsad medlemsåtkomst och behöver inte hoppa över JIT-synlighetskontroller.

    Inga särskilda behörigheter krävs för att generera en dynamisk metod, men den utgivna koden kräver de behörigheter som krävs av de typer och metoder som används. Om den utgivna koden till exempel anropar en metod som kommer åt en fil kräver den FileIOPermission. Om förtroendenivån inte innehåller den behörigheten utlöses ett säkerhetsfel när den kod som genereras körs. Koden som visas här genererar en dynamisk metod som endast använder metoden Console.WriteLine. Därför kan koden köras från delvis betrodda platser.

  • Du kan också skapa en anonymt värdbaserad dynamisk metod med begränsad möjlighet att hoppa över JIT-synlighetskontroller genom att använda DynamicMethod(String, Type, Type[], Boolean) konstruktorn och ange true för parametern restrictedSkipVisibility.

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

    Begränsningen är att den anonymt värdbaserade dynamiska metoden endast kan komma åt privata data i sammansättningar med förtroendenivåer som är lika med eller mindre än förtroendenivån för den avsändande sammansättningen. Om den dynamiska metoden till exempel körs med Internetförtroende kan den komma åt privata data i andra sammansättningar som också körs med Internetförtroende, men den kan inte komma åt privata data för .NET Framework-sammansättningar. .NET Framework-sammansättningar installeras i den globala sammansättningscachen och är alltid fullt betrodda.

    Anonymt värdbaserade dynamiska metoder kan använda den här begränsade möjligheten att hoppa över JIT-synlighetskontroller endast om värdprogrammet beviljar ReflectionPermission med flaggan ReflectionPermissionFlag.RestrictedMemberAccess. Kravet på den här behörigheten görs när metoden anropas.

    Anmärkning

    Information om anropsstacken för den utsändande sammansättningen ingår när den dynamiska metoden konstrueras. Därför görs efterfrågan mot behörigheterna för den avsändande sammansättningen i stället för den sammansättning som anropar metoden. Detta förhindrar att den kod som genereras körs med förhöjd behörighet.

    Det fullständiga kodexemplet i slutet av den här genomgången visar användningen och begränsningarna för begränsad medlemsåtkomst. Dess Worker-klass innehåller en metod som kan skapa anonymt värdbaserade dynamiska metoder med eller utan begränsad möjlighet att hoppa över synlighetskontroller, och exemplet visar resultatet av att köra den här metoden i programdomäner som har olika förtroendenivåer.

    Anmärkning

    Den begränsade möjligheten att hoppa över synlighetskontroller är en funktion i anonymt värdbaserade dynamiska metoder. När vanliga dynamiska metoder hoppar över JIT-synlighetskontroller måste de beviljas fullständigt förtroende.

Exempel

Beskrivning

Följande kodexempel visar hur flaggan RestrictedMemberAccess används för att tillåta anonymt värdbaserade dynamiska metoder att hoppa över JIT-synlighetskontroller, men bara när målmedlemmen har samma eller lägre förtroendenivå än den sammansättning som genererar koden.

I exemplet definieras en Worker-klass som kan ordnas över programdomängränser. Klassen har två AccessPrivateMethod metodöverbelastningar som genererar och kör dynamiska metoder. Den första överlagringen genererar en dynamisk metod som anropar den privata PrivateMethod-metoden i klassen Worker, och den kan generera den dynamiska metoden med eller utan JIT-synlighetskontroller. Den andra överlagringen genererar en dynamisk metod som får åtkomst till en internal egenskap (Friend egenskap i Visual Basic) för klassen String.

I exemplet används en hjälpmetod för att skapa en bidragsuppsättning som är begränsad till Internet behörigheter. Därefter skapas en applikationsdomän med hjälp av AppDomain.CreateDomain(String, Evidence, AppDomainSetup, PermissionSet, StrongName[]) metodöverlagring för att ange att all kod som körs i domänen använder denna bidragsuppsättning. Exemplet skapar en instans av klassen Worker i programdomänen och kör metoden AccessPrivateMethod två gånger.

  • Första gången metoden AccessPrivateMethod körs tillämpas JIT-synlighetskontroller. Den dynamiska metoden misslyckas när den anropas eftersom JIT-synlighetskontroller hindrar den från att komma åt den privata metoden.

  • När metoden AccessPrivateMethod körs för andra gången, hoppar JIT-synlighetskontrollerna över. Den dynamiska metoden misslyckas när den kompileras, eftersom Internet beviljar inte tillräckliga behörigheter för att hoppa över synlighetskontroller.

Exemplet lägger till ReflectionPermission och ReflectionPermissionFlag.RestrictedMemberAccess i behörighetsuppsättningen. Exemplet skapar sedan en andra domän och anger att all kod som körs i domänen beviljas behörigheterna i den nya beviljandeuppsättningen. Exemplet skapar en instans av klassen Worker i den nya programdomänen och kör båda överlagringarna av metoden AccessPrivateMethod.

  • Den första överbelastningen av metoden AccessPrivateMethod körs, och JIT-synlighetskontroller skippas. Den dynamiska metoden kompilerar och körs korrekt eftersom sammansättningen som genererar koden är densamma som den sammansättning som innehåller den privata metoden. Därför är förtroendenivåerna lika. Om programmet som innehåller klassen Worker hade flera sammansättningar skulle samma process lyckas för någon av dessa sammansättningar, eftersom alla skulle vara på samma förtroendenivå.

  • Den andra överbelastningen av metoden AccessPrivateMethod körs och igen hoppas JIT-synlighetskontroller över. Den här gången misslyckas den dynamiska metoden när den kompileras, eftersom den försöker komma åt egenskapen internalFirstChar för klassen String. Sammansättningen som innehåller klassen String är fullständigt betrodd. Därför är det på en högre förtroendenivå än den sammansättning som genererar koden.

Den här jämförelsen visar hur ReflectionPermissionFlag.RestrictedMemberAccess gör det möjligt för delvis betrodd kod att hoppa över synlighetskontroller för annan delvis betrodd kod utan att äventyra säkerheten för betrodd kod.

Kod

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($"{ex.GetType().Name} was thrown when the delegate was invoked.");
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"{ex.GetType().Name} was thrown when the delegate was compiled.");
        }
    }

    // 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($"{first} is the first character.");
        }
        catch (Exception ex)
        {
            Console.WriteLine($"{ex.GetType().Name} was thrown when the delegate was compiled.");
        }
    }

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

Kompilera koden

  • Om du skapar det här kodexemplet i Visual Studio måste du ändra namnet på klassen så att namnområdet inkluderas när du skickar det till metoden CreateInstanceAndUnwrap. Som standard är namnområdet namnet på projektet. Om projektet till exempel är "PartialTrust" måste klassnamnet vara "PartialTrust.Worker".

Se även