Udostępnij za pośrednictwem


Przewodnik: emitowanie kodu w scenariuszach częściowego zaufania

Emitowanie refleksji wykorzystuje ten sam zestaw interfejsów API przy pełnym lub częściowym zaufaniu środowiska, ale niektóre funkcje wymagają specjalnych uprawnień w częściowo zaufanym środowisku. Ponadto emisja odbicia ma funkcjonalność anonimowo hostowanych metod dynamicznych, która ma być używana z częściowym zaufaniem i przez zestawy przezroczyste dla zabezpieczeń.

Uwaga

Przed .NET Frameworkiem 3.5 emitowanie kodu wymagało ReflectionPermission z flagą ReflectionPermissionFlag.ReflectionEmit. To uprawnienie jest domyślnie uwzględniane w FullTrust i Intranet nazwanych zestawach uprawnień, ale nie w zestawie uprawnień Internet. W związku z tym z biblioteki można korzystać z częściowego zaufania tylko wtedy, gdy miała atrybut SecurityCriticalAttribute i wykonywała metodę Assert dla ReflectionEmit. Takie biblioteki wymagają dokładnego przeglądu zabezpieczeń, ponieważ błędy kodowania mogą powodować luki w zabezpieczeniach. Program .NET Framework 3.5 umożliwia emitowanie kodu w scenariuszach częściowego zaufania bez wystawiania żadnych żądań zabezpieczeń, ponieważ generowanie kodu nie jest z natury operacją uprzywilejowaną. Oznacza to, że wygenerowany kod nie ma więcej uprawnień niż zestaw, który go emituje. Dzięki temu biblioteki, które emitują kod, mogą być przezroczyste dla zabezpieczeń i usuwa to potrzebę potwierdzenia ReflectionEmit, dzięki czemu pisanie bezpiecznej biblioteki nie wymaga szczegółowego przeglądu zabezpieczeń.

W tym przewodniku przedstawiono następujące zadania:

Aby uzyskać więcej informacji na temat emitowania kodu w scenariuszach częściowego zaufania, zobacz Problemy z zabezpieczeniami w emitowaniu odbicia.

Aby uzyskać pełną listę kodu pokazanego w tych procedurach, zobacz sekcję Przykład na końcu tego przewodnika.

Konfigurowanie częściowo zaufanych lokalizacji

W poniższych dwóch procedurach pokazano, jak skonfigurować lokalizacje, z których można przetestować kod z częściowym zaufaniem.

  • Pierwsza procedura pokazuje, jak utworzyć domenę aplikacji w środowisku piaskownicy, w której kod otrzymuje uprawnienia internetowe.

  • Druga procedura pokazuje, jak dodać ReflectionPermission z flagą ReflectionPermissionFlag.RestrictedMemberAccess do częściowo zaufanej domeny aplikacji, aby umożliwić dostęp do danych prywatnych w zestawach o równym lub mniejszym zaufaniu.

Tworzenie domen aplikacyjnych w izolowanym trybie

Aby utworzyć domenę aplikacji, w której zestawy działają z częściowym zaufaniem, należy określić zestaw uprawnień, które mają zostać przyznane zestawom przy użyciu przeciążenia metody AppDomain.CreateDomain(String, Evidence, AppDomainSetup, PermissionSet, StrongName[]) w celu utworzenia domeny aplikacji. Najprostszym sposobem określenia zestawu przydziałów jest pobranie nazwanego zestawu uprawnień z zasad zabezpieczeń.

Poniższa procedura tworzy domenę aplikacji w środowisku izolowanym, która uruchamia Twój kod z częściowym zaufaniem, aby przetestować scenariusze, w których emitowany kod może uzyskiwać dostęp tylko do publicznych członków typów publicznych. W kolejnej procedurze pokazano, jak dodać RestrictedMemberAccess, aby przetestować scenariusze, w których emitowany kod może uzyskiwać dostęp do typów niepublicowych i elementów członkowskich w zestawach, którym przyznano równe lub mniejsze uprawnienia.

Aby utworzyć domenę aplikacji z częściowym zaufaniem

  1. Utwórz zestaw uprawnień dla zestawów w domenie aplikacji w trybie piaskownicy. W tym przypadku jest używany zestaw uprawnień strefy internetowej.

    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. Utwórz obiekt AppDomainSetup w celu zainicjowania domeny aplikacji przy użyciu ścieżki aplikacji.

    Ważne

    Dla uproszczenia w tym przykładzie kodu jest używany bieżący folder. Aby uruchomić kod, który faktycznie pochodzi z Internetu, użyj oddzielnego folderu dla niezaufanego kodu, zgodnie z opisem w Instrukcje: uruchamianie częściowo zaufanego kodu w piaskownicy.

    AppDomainSetup adSetup = new AppDomainSetup();
    adSetup.ApplicationBase = ".";
    
    Dim adSetup As New AppDomainSetup()
    adSetup.ApplicationBase = "."
    
  3. Utwórz domenę aplikacji, określając informacje o konfiguracji domeny aplikacji i zestaw uprawnień dla wszystkich zestawów wykonywanych w domenie aplikacji.

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

    Ostatni parametr przeciążenia metody AppDomain.CreateDomain(String, Evidence, AppDomainSetup, PermissionSet, StrongName[]) umożliwia określenie zestawu bibliotek, które mają zostać obdarzone pełnym zaufaniem, zamiast poziomu uprawnień domeny aplikacji. Nie trzeba określać zestawów programu .NET Framework używanych przez aplikację, ponieważ te zestawy znajdują się w globalnej pamięci podręcznej zestawów. Zestawy w globalnej pamięci podręcznej zestawów są zawsze w pełni zaufane. Ten parametr służy do określania zestawów o silnych nazwach, które nie znajdują się w globalnej pamięci podręcznej zestawów (GAC).

Dodawanie funkcji "RestrictedMemberAccess" do domen piaskownicy

Aplikacje hosta mogą zezwalać anonimowo hostowanym metodom dynamicznym na dostęp do danych prywatnych w zestawach, które mają poziomy zaufania równe lub mniejsze niż poziom zaufania zestawu, który emituje kod. Aby włączyć tę ograniczoną możliwość pomijania kontroli widoczności just in time (JIT), aplikacja hosta dodaje obiekt ReflectionPermission z flagą ReflectionPermissionFlag.RestrictedMemberAccess (RMA) do zestawu uprawnień.

Na przykład host może przyznać aplikacjom internetowym uprawnienia internetowe plus RMA, aby aplikacja internetowa mogła emitować kod, który uzyskuje dostęp do prywatnych danych we własnych zestawach aplikacyjnych. Ponieważ dostęp jest ograniczony do zestawów o równym lub mniejszym zaufaniu, aplikacja internetowa nie może uzyskać dostępu do elementów członkowskich w pełni zaufanych zestawów, takich jak zestawy programu .NET Framework.

Uwaga

Aby zapobiec podwyższeniu uprawnień, informacje o stosie dla zestawu emitującego są uwzględniane w trakcie tworzenia anonimowo hostowanych metod dynamicznych. Po wywołaniu metody informacje o stosie są sprawdzane. W związku z tym dynamiczna metoda hostowana anonimowo, wywoływana z w pełni zaufanego kodu, jest nadal ograniczona do poziomu zaufania emitującego zestawu.

Aby utworzyć domenę aplikacji z częściowym zaufaniem oraz RMA

  1. Utwórz nowy obiekt ReflectionPermission z flagą RestrictedMemberAccess (RMA) i użyj metody PermissionSet.SetPermission, aby dodać uprawnienie do zestawu uprawnień.

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

    Metoda AddPermission dodaje uprawnienie do zestawu przyznanych uprawnień, jeśli nie jest już uwzględnione. Jeśli uprawnienie jest już uwzględnione w zestawie dotacji, określone flagi są dodawane do istniejącego uprawnienia.

    Uwaga

    RMA to funkcja anonimowo hostowanych metod dynamicznych. Gdy zwykłe metody dynamiczne pomijają sprawdzanie widoczności trybu JIT, emitowany kod wymaga pełnego zaufania.

  2. Utwórz domenę aplikacji, określając informacje o konfiguracji domeny aplikacji i zestaw dotacji.

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

Uruchamianie kodu w domenach aplikacji w trybie piaskownicy

Poniższa procedura wyjaśnia, jak zdefiniować klasę przy użyciu metod, które można wykonać w domenie aplikacji, jak utworzyć wystąpienie klasy w domenie oraz jak wykonać jej metody.

Aby zdefiniować i wykonać metodę w domenie aplikacji

  1. Zdefiniuj klasę pochodzącą z MarshalByRefObject. Dzięki temu można tworzyć wystąpienia klasy w innych domenach aplikacji i wykonywać wywołania metod w granicach domeny aplikacji. Klasa w tym przykładzie nosi nazwę Worker.

    public class Worker : MarshalByRefObject
    {
    
    Public Class Worker
        Inherits MarshalByRefObject
    
  2. Zdefiniuj publiczną metodę zawierającą kod, który chcesz wykonać. W tym przykładzie kod emituje prostą metodę dynamiczną, tworzy delegata, który wykonuje metodę, i wywołuje delegata.

    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. W swoim głównym programie uzyskaj wyświetlaną nazwę swojego zestawu. Ta nazwa jest używana podczas tworzenia wystąpień klasy Worker wewnątrz domeny aplikacji działającej w piaskownicy.

    String asmName = typeof(Worker).Assembly.FullName;
    
    Dim asmName As String = GetType(Worker).Assembly.FullName
    
  4. W głównym programie utwórz domenę aplikacji w trybie piaskownicy zgodnie z opisem w pierwszej procedury w tym przewodniku. Nie musisz dodawać żadnych uprawnień do zestawu uprawnień Internet, ponieważ metoda SimpleEmitDemo używa tylko metod publicznych.

  5. W głównym programie utwórz wystąpienie klasy Worker w środowisku piaskownicy aplikacji.

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

    Metoda CreateInstanceAndUnwrap tworzy obiekt w domenie aplikacji docelowej i zwraca serwer proxy, który może służyć do wywoływania właściwości i metod obiektu.

    Uwaga

    Jeśli używasz tego kodu w programie Visual Studio, musisz zmienić nazwę klasy, aby uwzględnić przestrzeń nazw. Domyślnie przestrzeń nazw jest nazwą projektu. Jeśli na przykład projekt nosi nazwę "PartialTrust", nazwa klasy powinna być "PartialTrust.Worker".

  6. Dodaj kod, aby wywołać metodę SimpleEmitDemo. Wywołanie jest przekazywane przez granicę domeny aplikacji, a kod jest wykonywany w piaskownicowej domenie aplikacji.

    w.SimpleEmitDemo();
    
    w.SimpleEmitDemo()
    

Używanie anonimowo hostowanych metod dynamicznych

Metody dynamiczne hostowane anonimowo są skojarzone z przezroczystym zestawem dostarczanym przez system. W związku z tym kod, który zawiera, jest przezroczysty. Z drugiej strony zwykłe metody dynamiczne muszą być skojarzone z istniejącym modułem (określonym bezpośrednio lub wywnioskowanym z skojarzonego typu) i pobrać z tego modułu poziom zabezpieczeń.

Uwaga

Jedynym sposobem skojarzenia metody dynamicznej z zestawem, który zapewnia hosting anonimowy, jest użycie konstruktorów opisanych w poniższej procedurze. Nie można jawnie określić modułu w zestawie hostingu anonimowego.

Standardowe metody dynamiczne mają dostęp do elementów wewnętrznych modułu, z którymi są skojarzone, albo do elementów prywatnych typu, z którymi są skojarzone. Ponieważ metody dynamiczne hostowane anonimowo są odizolowane od innego kodu, nie mają dostępu do danych prywatnych. Mają jednak ograniczoną możliwość pomijania kontroli widoczności JIT w celu uzyskania dostępu do danych prywatnych. Ta możliwość jest ograniczona do zestawów, które mają poziomy zaufania równe lub mniejsze niż poziom zaufania zestawu, który emituje kod.

Aby zapobiec podwyższeniu uprawnień, informacje o stosie dla emitowanego zestawu są uwzględniane podczas konstruowania anonimowo hostowanych metod dynamicznych. Po wywołaniu metody informacje o stosie są sprawdzane. Anonimowo hostowana metoda dynamiczna wywoływana z w pełni zaufanego kodu jest nadal ograniczona do poziomu zaufania zestawu, który go emitował.

Aby używać metod dynamicznych hostowanych anonimowo

  • Utwórz metodę dynamiczną hostowaną anonimowo przy użyciu konstruktora, który nie określa skojarzonego modułu ani typu.

    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)
    

    Jeśli metoda dynamiczna hostowana anonimowo używa tylko publicznych typów i metod, nie wymaga ograniczonego dostępu do składowych i nie musi pomijać kontroli widoczności JIT.

    Do emitowania metody dynamicznej nie są wymagane żadne specjalne uprawnienia, ale emitowany kod wymaga uprawnień wymaganych przez typy i metody, których używa. Jeśli na przykład emitowany kod wywołuje metodę, która uzyskuje dostęp do pliku, wymaga FileIOPermission. Jeśli poziom zaufania nie obejmuje tego uprawnienia, podczas wykonywania emitowanego kodu zgłaszany jest wyjątek zabezpieczeń. Pokazany tutaj kod emituje metodę dynamiczną, która używa tylko metody Console.WriteLine. W związku z tym kod można wykonać z częściowo zaufanych lokalizacji.

  • Alternatywnie utwórz anonimowo hostowaną metodę dynamiczną z ograniczoną możliwością pomijania kontroli widoczności JIT przy użyciu konstruktora DynamicMethod(String, Type, Type[], Boolean) i określania true dla parametru restrictedSkipVisibility.

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

    Ograniczenie polega na tym, że anonimowo hostowana metoda dynamiczna może uzyskiwać dostęp do danych prywatnych tylko w zestawach z poziomami zaufania równymi lub mniejszymi od poziomu zaufania zestawu emitującego. Na przykład, jeśli metoda dynamiczna działa z poziomem zaufania internetowego, może uzyskać dostęp do prywatnych danych w innych zestawach, które również działają z zaufaniem internetowym, ale nie może uzyskać dostępu do prywatnych danych zestawów .NET Framework. Zestawy programu .NET Framework są instalowane w globalnej pamięci podręcznej zestawów i są zawsze w pełni zaufane.

    Metody dynamiczne anonimowo hostowane mogą skorzystać z tej ograniczonej zdolności do pomijania kontroli widoczności JIT, tylko wtedy, gdy aplikacja hostująca udziela zgody ReflectionPermission z flagą ReflectionPermissionFlag.RestrictedMemberAccess. Żądanie tego uprawnienia jest składane w momencie wywołania metody.

    Uwaga

    Informacje o stosie wywołań dla zestawu emitowania są uwzględniane podczas konstruowania metody dynamicznej. W związku z tym żąda się względem uprawnień emitującego zestawu, niż zestawu wywołującego metodę. Zapobiega to wykonywaniu emitowanego kodu z podwyższonym poziomem uprawnień.

    Kompletny przykład kodu znajdujący się na końcu przewodnika pokazuje użycie i ograniczenia dostępu do ograniczonych elementów składowych. Klasa Worker zawiera metodę, która umożliwia tworzenie anonimowo hostowanych metod dynamicznych z ograniczoną możliwością pomijania kontroli widoczności, a w przykładzie przedstawiono wynik wykonywania tej metody w domenach aplikacji, które mają różne poziomy zaufania.

    Uwaga

    Ograniczona możliwość pomijania kontroli widoczności to funkcja anonimowo hostowanych metod dynamicznych. Kiedy zwykłe metody dynamiczne pomijają kontrole widoczności JIT, muszą uzyskać pełne zaufanie.

Przykład

Opis

W poniższym przykładzie kodu pokazano użycie flagi RestrictedMemberAccess w celu umożliwienia anonimowo hostowanych metod dynamicznych pomijania kontroli widoczności JIT, ale tylko wtedy, gdy element członkowski docelowy jest na równym lub niższym poziomie zaufania niż zestaw emitujący kod.

W tym przykładzie zdefiniowano klasę Worker, którą można rozmieścić w granicach domeny aplikacji. Klasa ma dwa przeciążenia metod AccessPrivateMethod, które generują i wykonują metody dynamiczne. Pierwsze przeciążenie emituje metodę dynamiczną, która wywołuje prywatną metodę PrivateMethod klasy Worker i może emitować tę metodę dynamiczną z kontrolą widoczności JIT lub bez niej. Drugie przeciążenie emituje metodę dynamiczną, która uzyskuje dostęp do właściwości internal klasy String (w Visual Basic właściwościFriend tej samej klasy).

W przykładzie użyto metody pomocniczej, aby utworzyć zestaw uprawnień ograniczony do uprawnień Internet, a następnie jest tworzona domena aplikacji przy użyciu przeciążenia metody AppDomain.CreateDomain(String, Evidence, AppDomainSetup, PermissionSet, StrongName[]), aby określić, że cały kod wykonywany w domenie używa tego zestawu uprawnień. Przykład tworzy wystąpienie klasy Worker w domenie aplikacji i wykonuje metodę AccessPrivateMethod dwa razy.

  • Po pierwszym wykonaniu metody AccessPrivateMethod wymuszane są kontrole widoczności JIT. Metoda dynamiczna kończy się niepowodzeniem w momencie jej wywołania, ponieważ kontrole widoczności JIT uniemożliwiają dostęp do metody prywatnej.

  • Po drugim wykonaniu metody AccessPrivateMethod sprawdzanie widoczności JIT jest pomijane. Metoda dynamiczna zawodzi podczas kompilacji, ponieważ zestaw uprawnień Internet nie uzyskał wystarczających uprawnień, aby pominąć sprawdzanie widoczności.

W przykładzie dodano ReflectionPermission i ReflectionPermissionFlag.RestrictedMemberAccess do przyznanego zestawu. W tym przykładzie zostanie utworzona druga domena, określając, że wszystkie kody wykonywane w domenie mają przyznane uprawnienia w nowym zestawie dotacji. Przykład tworzy wystąpienie klasy Worker w nowej domenie aplikacji i wykonuje oba przeciążenia metody AccessPrivateMethod.

  • Wykonywane jest pierwsze przeciążenie metody AccessPrivateMethod, a kontrole widoczności JIT są pomijane. Metoda dynamiczna kompiluje i wykonuje pomyślnie, ponieważ zestaw, który emituje kod, jest taki sam jak zestaw zawierający metodę prywatną. W związku z tym poziomy zaufania są równe. Jeśli aplikacja zawierająca klasę Worker miała kilka zestawów, ten sam proces powiedzie się dla każdego z tych zestawów, ponieważ wszystkie te zestawy byłyby na tym samym poziomie zaufania.

  • Drugie przeciążenie metody AccessPrivateMethod jest wykonywane, a sprawdzanie widoczności przez JIT jest ponownie pomijane. Tym razem metoda dynamiczna kończy się niepowodzeniem podczas kompilowania, ponieważ próbuje uzyskać dostęp do właściwości internalFirstChar klasy String. Zestaw zawierający klasę String jest w pełni zaufany. W związku z tym jest on na wyższym poziomie zaufania niż zestaw, który emituje kod.

To porównanie pokazuje, jak ReflectionPermissionFlag.RestrictedMemberAccess umożliwia pomijanie przez częściowo zaufany kod sprawdzania widoczności dla innego częściowo zaufanego kodu, bez naruszania zabezpieczeń kodu zaufanego.

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

Kompilowanie kodu

  • Jeśli utworzysz ten przykład kodu w programie Visual Studio, musisz zmienić nazwę klasy, aby uwzględnić przestrzeń nazw podczas przekazywania jej do metody CreateInstanceAndUnwrap. Domyślnie przestrzeń nazw jest nazwą projektu. Na przykład, jeśli projekt jest "PartialTrust", nazwa klasy musi być "PartialTrust.Worker".

Zobacz też