Freigeben über


Exemplarische Vorgehensweise: Ausgeben von Code in Szenarios mit teilweiser Vertrauenswürdigkeit

Die Reflektionsausgabe verwendet für volle oder teilweise Vertrauenswürdigkeit den gleichen API-Satz, für teilweise vertrauenswürdigen Code erfordern einige Funktionen allerdings besondere Berechtigungen. Außerdem verfügt die Reflektionsausgabe über eine Funktion für anonym gehostete dynamische Methoden, die zur Verwendung mit teilweiser Vertrauenswürdigkeit und sicherheitstransparenten Assemblys vorgesehen ist.

Hinweis

Vor .NET Framework 3.5 war zur Ausgabe von Code ReflectionPermission mit dem ReflectionPermissionFlag.ReflectionEmit-Flag erforderlich. Diese Berechtigung ist standardmäßig im benannten FullTrust- und Intranet-Berechtigungssatz enthalten, jedoch nicht im Internet-Berechtigungssatz. Daher kann eine Bibliothek nur mit teilweiser Vertrauenswürdigkeit verwendet werden, wenn sie über das SecurityCriticalAttribute-Attribut verfügt und eine Assert-Methode für ReflectionEmit ausgeführt hat. Diese Bibliotheken erfordern einen sorgfältigen Sicherheitsreview, da Codierungsfehler zu Sicherheitslücken führen können. In .NET Framework 3.5 ist es möglich, Code in teilweise vertrauenswürdigen Szenarios ohne Sicherheitsanforderungen auszugeben, da das Generieren von Code an sich keinen privilegierten Vorgang darstellt. Das bedeutet, dass der generierte Code nicht mehr Berechtigungen aufweist als die Assembly, die ihn ausgibt. Bibliotheken, die Code ausgeben, können somit sicherheitstransparent sein und setzen keine ReflectionEmit-Assertion voraus, sodass zum Schreiben einer sicheren Bibliothek keine genaue Sicherheitsüberprüfung erforderlich ist.

In dieser exemplarischen Vorgehensweise werden die folgenden Aufgaben veranschaulicht:

Weitere Informationen zum Ausgeben von Code in Szenarios mit teilweiser Vertrauenswürdigkeit finden Sie unter Security Issues in Reflection Emit (Sicherheitsaspekte bei der Reflektionsausgabe).

Eine vollständige Auflistung des in diesen Verfahren dargestellten Codes finden Sie im Beispielabschnitt am Ende dieser exemplarischen Vorgehensweise.

Einrichten von teilweise vertrauenswürdigen Speicherorten

Die folgenden beiden Verfahren enthalten Informationen zum Einrichten von Speicherorten, über die teilweise vertrauenswürdiger Code getestet werden kann.

  • Im ersten Verfahren wird beschrieben, wie Sie eine Sandboxanwendungsdomäne erstellen, in der dem Code Internetberechtigungen zugewiesen werden.

  • Im zweiten Verfahren wird beschrieben, wie Sie einer teilweise vertrauenswürdigen Anwendungsdomäne ReflectionPermission mit dem ReflectionPermissionFlag.RestrictedMemberAccess-Flag hinzufügen, um Zugriff auf private Daten in Assemblys mit gleicher oder geringerer Vertrauenswürdigkeit zu ermöglichen.

Erstellen von Sandbox-Anwendungsdomänen

Um eine Anwendungsdomäne zu erstellen, in der Ihre Assemblys mit teilweiser Vertrauenswürdigkeit ausgeführt werden, müssen Sie den Satz der Berechtigungen, die den Assemblys erteilt werden sollen, mithilfe der AppDomain.CreateDomain(String, Evidence, AppDomainSetup, PermissionSet, StrongName[])-Methodenüberladung angeben. Am einfachsten ist es, einen benannten Berechtigungssatz aus der Sicherheitsrichtlinie abzurufen.

Im folgenden Verfahren wird eine Sandbox-Anwendungsdomäne erstellt, die Code mit teilweiser Vertrauenswürdigkeit ausführt, um Szenarios zu testen, in denen ausgegebener Code nur auf öffentliche Member öffentlicher Typen zugreifen kann. In einem späteren Verfahren wird beschrieben, wie Sie RestrictedMemberAccess hinzufügen, um Szenarios zu testen, in denen ausgegebener Code auf nicht öffentliche Typen und Member in Assemblys zugreifen kann, denen die gleichen oder geringere Berechtigungen erteilt wurden.

So erstellen Sie eine Anwendungsdomäne mit teilweiser Vertrauenswürdigkeit

  1. Erstellen Sie einen Berechtigungssatz, der den Assemblys in der Sandkastenanwendungsdomäne gewährt wird. In diesem Fall wird der Berechtigungssatz der Internetzone verwendet.

    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. Erstellen Sie ein AppDomainSetup-Objekt, um die Anwendungsdomäne mit einem Anwendungspfad zu initialisieren.

    Wichtig

    In diesem Codebeispiel wird der Einfachheit halber der aktuelle Ordner verwendet. Um Code auszuführen, der eigentlich aus dem Internet stammt, verwenden Sie einen separaten Ordner für den nicht vertrauenswürdigen Code, wie in Vorgehensweise: Ausführen von teilweise vertrauenswürdigem Code in einem Sandkasten beschrieben.

    AppDomainSetup adSetup = new AppDomainSetup();
    adSetup.ApplicationBase = ".";
    
    Dim adSetup As New AppDomainSetup()
    adSetup.ApplicationBase = "."
    
  3. Erstellen Sie die Anwendungsdomäne, indem Sie die Setupinformationen der Anwendungsdomäne und den Berechtigungssatz für alle Assemblys angeben, die in der Anwendungsdomäne ausgeführt werden.

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

    Mit dem letzten Parameter der AppDomain.CreateDomain(String, Evidence, AppDomainSetup, PermissionSet, StrongName[])-Methodenüberladung können Sie anstelle des Berechtigungssatzes der Anwendungsdomäne eine Gruppe von Assemblys angeben, denen volle Vertrauenswürdigkeit erteilt werden soll. Die von der Anwendung verwendeten .NET Framework-Assemblys müssen Sie nicht angeben, da diese Assemblys im globalen Assemblycache enthalten sind. Assemblys im globalen Assemblycache sind immer voll vertrauenswürdig. Sie können diesen Parameter verwenden, um Assemblys mit starkem Namen anzugeben, die nicht im globalen Assemblycache enthalten sind.

Hinzufügen von eingeschränktem Memberzugriff auf Sandboxdomänen

Hostanwendungen können anonym gehosteten dynamischen Methoden Zugriff auf private Daten in Assemblys ermöglichen, die die gleiche oder eine geringere Vertrauensebene als die Assembly aufweisen, die den Code ausgibt. Um diese eingeschränkte Fähigkeit zum Überspringen von JIT-Sichtbarkeitsprüfungen zu ermöglichen, fügt die Hostanwendung dem Berechtigungssatz ein ReflectionPermission-Objekt mit dem ReflectionPermissionFlag.RestrictedMemberAccess-Flag hinzu.

Beispielsweise kann ein Host Internetanwendungen sowohl Internetberechtigungen als auch eingeschränkten Memberzugriff (RestrictedMemberAccess, RMA) erteilen, sodass eine Internetanwendung Code ausgeben kann, der auf private Daten in den eigenen Assemblys zugreift. Da der Zugriff auf Assemblys mit gleicher oder geringerer Vertrauenswürdigkeit beschränkt ist, kann eine Internetanwendung nicht auf Member voll vertrauenswürdiger Assemblys zugreifen, z. B. .NET Framework-Assemblys.

Hinweis

Um Rechteerweiterungen zu verhindern, werden beim Erstellen anonym gehosteter dynamischer Methoden Stapelinformationen für die ausgebende Assembly einbezogen. Wenn die Methode aufgerufen wird, werden die Stapelinformationen überprüft. Daher ist eine anonym gehostete dynamische Methode, die über voll vertrauenswürdigen Code aufgerufen wird, auf die Vertrauensebene der ausgebenden Assembly beschränkt.

So erstellen Sie eine Anwendungsdomäne mit teilweiser Vertrauenswürdigkeit und eingeschränktem Memberzugriff

  1. Erstellen Sie ein neues ReflectionPermission-Objekt mit dem RestrictedMemberAccess (RMA)-Flag, und fügen Sie die Berechtigung mit der PermissionSet.SetPermission-Methode dem Berechtigungssatz hinzu.

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

    Wenn die Berechtigung noch nicht im Berechtigungssatz enthalten ist, wird sie mit der AddPermission-Methode hinzugefügt. Wenn die Berechtigung bereits im Berechtigungssatz enthalten ist, werden die angegebenen Flags der vorhandenen Berechtigung hinzugefügt.

    Hinweis

    Das Feature des eingeschränkten Memberzugriffs ist ein Feature anonym gehosteter dynamischer Methoden. Wenn gewöhnliche dynamische Methoden die JIT-Sichtbarkeitsüberprüfungen überspringen, erfordert der ausgegebene Code volle Vertrauenswürdigkeit.

  2. Erstellen Sie die Anwendungsdomäne, indem Sie die Setupinformationen für die Anwendungsdomäne und den Berechtigungssatz angeben.

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

Ausführen von Code in Sandbox-Anwendungsdomänen

Im folgenden Verfahren wird beschrieben, wie Sie eine Klasse mit Methoden definieren, die in einer Anwendungsdomäne ausgeführt werden können, wie Sie eine Instanz dieser Klasse in der Domäne erstellen und wie Sie deren Methoden ausführen.

So definieren Sie eine Methode in einer Anwendungsdomäne und führen sie aus

  1. Definieren Sie eine Klasse, die sich von MarshalByRefObject ableitet. Auf diese Weise können Sie Instanzen der Klasse in anderen Anwendungsdomänen erstellen und Methoden über die Grenzen der Anwendungsdomäne hinweg aufrufen. Die Klasse in diesem Beispiel hat den Namen Worker.

    public class Worker : MarshalByRefObject
    {
    
    Public Class Worker
        Inherits MarshalByRefObject
    
  2. Definieren Sie eine öffentliche Methode, die den Code enthält, den Sie ausführen möchten. In diesem Beispiel gibt der Code eine einfache dynamische Methode aus, erstellt einen Delegaten zum Ausführen der Methode und ruft den Delegaten auf.

    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. Rufen Sie im Hauptprogramm den Anzeigenamen der Assembly ab. Dieser Name wird verwendet, wenn Sie Instanzen der Worker-Klasse in der Sandbox-Anwendungsdomäne erstellen.

    String asmName = typeof(Worker).Assembly.FullName;
    
    Dim asmName As String = GetType(Worker).Assembly.FullName
    
  4. Erstellen Sie im Hauptprogramm eine Sandbox-Anwendungsdomäne, wie in der ersten Prozedur dieser exemplarischen Vorgehensweise beschrieben. Sie müssen dem Internet-Berechtigungssatz keine Berechtigungen hinzufügen, da die SimpleEmitDemo-Methode nur öffentliche Methoden verwendet.

  5. Erstellen Sie im Hauptprogramm eine Instanz der Worker-Klasse in der Sandbox-Anwendungsdomäne.

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

    Die CreateInstanceAndUnwrap-Methode erstellt das Objekt in der Zielanwendungsdomäne und gibt einen Proxy zurück, der zum Aufrufen der Eigenschaften und Methoden des Objekts verwendet werden kann.

    Hinweis

    Wenn Sie diesen Code in Visual Studio verwenden, müssen Sie den Namen der Klasse ändern, sodass der Namespace enthalten ist. Standardmäßig ist der Namespace der Name des Projekts. Wenn das Projekt z. B. "PartialTrust" heißt, muss der Klassenname "PartialTrust.Worker" lauten.

  6. Fügen Sie Code zum Aufrufen der SimpleEmitDemo-Methode hinzu. Der Aufruf wird über die Grenze der Anwendungsdomäne hinweg gemarshallt, und der Code wird in der Sandbox-Anwendungsdomäne ausgeführt.

    w.SimpleEmitDemo();
    
    w.SimpleEmitDemo()
    

Verwenden von anonym gehosteten dynamischen Methoden

Anonym gehostete dynamische Methoden werden einer transparenten Assembly zugeordnet, die vom System bereitgestellt wird. Daher ist der Code, den sie enthalten, transparent. Gewöhnliche dynamische Methoden müssen andererseits einem vorhandenen Modul (das entweder direkt angegeben oder von einem zugeordneten Typ abgeleitet wird) zugeordnet sein und übernehmen ihre Sicherheitsstufe von diesem Modul.

Hinweis

Die einzige Möglichkeit, der Assembly, die anonymes Hosting bereitstellt, eine dynamische Methode zuzuordnen, ist die Verwendung der in den folgenden Verfahren beschriebenen Konstruktoren. Es ist nicht möglich, ein Modul in der anonymen Hostingassembly explizit anzugeben.

Normale dynamische Methoden verfügen über Zugriff auf die internen Member des Moduls, dem sie zugeordnet sind, oder auf die privaten Member des Typs, dem sie zugeordnet sind. Da anonym gehostete dynamische Methoden von anderem Code isoliert sind, verfügen sie über keinen Zugriff auf private Daten. Sie besitzen jedoch die eingeschränkte Fähigkeit, JIT-Sichtbarkeitsprüfungen zu überspringen, um Zugriff auf private Daten zu erhalten. Diese Möglichkeit ist auf Assemblys beschränkt, die die gleiche oder eine geringere Vertrauensebene aufweisen als die Assembly, die den Code ausgibt.

Um Rechteerweiterungen zu verhindern, werden beim Erstellen anonym gehosteter dynamischer Methoden Stapelinformationen für die ausgebende Assembly einbezogen. Wenn die Methode aufgerufen wird, werden die Stapelinformationen überprüft. Eine anonym gehostete dynamische Methode, die über voll vertrauenswürdigen Code aufgerufen wird, ist auf die Vertrauensebene der ausgebenden Assembly beschränkt.

So verwenden Sie anonym gehostete dynamische Methoden

  • Erstellen Sie eine anonym gehostete dynamische Methode, indem Sie einen Konstruktor verwenden, der kein zugeordnetes Modul und keinen zugeordneten Typ angibt.

    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)
    

    Wenn eine anonym gehostete dynamische Methode nur öffentliche Typen und Methoden verwendet, erfordert sie keinen eingeschränkten Memberzugriff und muss keine JIT-Sichtbarkeitsprüfungen überspringen.

    Zur Ausgabe einer dynamischen Methode sind keine speziellen Berechtigungen erforderlich. Der ausgegebene Code erfordert jedoch die Berechtigungen, die von den verwendeten Typen und Methoden verlangt werden. Wenn der ausgegebene Code z. B. eine Methode aufruft, die auf eine Datei zugreift, erfordert er FileIOPermission. Wenn diese Berechtigung in der Vertrauensebene nicht enthalten ist, wird eine Sicherheitsausnahme ausgelöst, sobald der ausgegebene Code ausgeführt wird. Der hier dargestellte Code gibt eine dynamische Methode aus, die nur die Console.WriteLine-Methode verwendet. Daher kann der Code über teilweise vertrauenswürdige Speicherorte ausgeführt werden.

  • Alternativ können Sie eine anonym gehostete dynamische Methode mit eingeschränkter Fähigkeit zum Überspringen von JIT-Sichtbarkeitsprüfungen erstellen, indem Sie den DynamicMethod(String, Type, Type[], Boolean)-Konstruktor verwenden und true für den restrictedSkipVisibility-Parameter angeben.

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

    Die Einschränkung besteht darin, dass die anonym gehostete dynamische Methode nur auf private Daten in Assemblys zugreifen kann, die die gleiche oder eine geringere Vertrauensebene als die ausgebende Assembly aufweisen. Wenn die dynamische Methode z. B. mit Internet-Vertrauenswürdigkeit ausgeführt wird, kann sie auf private Daten in Assemblys zugreifen, die ebenfalls mit Internet-Vertrauenswürdigkeit ausgeführt werden, aber nicht auf private Daten in .NET Framework-Assemblys. .NET Framework-Assemblys werden im globalen Assemblycache installiert und sind immer voll vertrauenswürdig.

    Anonym gehostete dynamische Methoden können diese eingeschränkte Fähigkeit zum Überspringen von JIT-Sichtbarkeitsprüfungen nur verwenden, wenn die Hostanwendung ReflectionPermission mit dem ReflectionPermissionFlag.RestrictedMemberAccess-Flag erteilt. Diese Berechtigung wird angefordert, wenn die Methode aufgerufen wird.

    Hinweis

    Beim Erstellen der dynamischen Methode werden Aufruflisteninformationen für die ausgebende Assembly einbezogen. Daher bezieht sich die Anforderung auf die Berechtigungen der ausgebenden Assembly und nicht der Assembly, die die Methode aufruft. Somit wird verhindert, dass der ausgegebene Code mit erweiterten Berechtigungen ausgeführt wird.

    Das vollständige Codebeispiel am Ende dieser exemplarischen Vorgehensweise veranschaulicht die Verwendung und die Einschränkungen des eingeschränkten Memberzugriffs. Die dort verwendete Worker-Klasse beinhaltet eine Methode, die anonym gehostete dynamische Methoden mit oder ohne eingeschränkte Fähigkeit zum Überspringen von Sichtbarkeitsprüfungen erstellen kann. Das Beispiel zeigt das Ergebnis der Ausführung dieser Methode in Anwendungsdomänen mit verschiedenen Vertrauensebenen.

    Hinweis

    Die eingeschränkte Fähigkeit zum Überspringen von Sichtbarkeitsprüfungen ist ein Feature anonym gehosteter dynamischer Methoden. Wenn gewöhnliche dynamische Methoden die JIT-Sichtbarkeitsprüfungen überspringen, muss ihnen volle Vertrauenswürdigkeit gewährt werden.

Beispiel

Beschreibung

Das folgende Codebeispiel veranschaulicht die Verwendung des RestrictedMemberAccess-Flags, um anonym gehosteten dynamischen Methoden das Überspringen von JIT-Sichtbarkeitsprüfungen zu ermöglichen, wenn der Zielmember die gleiche oder eine geringere Vertrauensebene aufweist als die Assembly, die den Code ausgibt.

Im Beispiel wird eine Worker-Klasse definiert, die über die Grenzen der Anwendungsdomäne hinweg gemarshallt werden kann. Die Klasse verfügt über zwei AccessPrivateMethod-Methodenüberladungen, die dynamische Methoden ausgeben und ausführen. Die erste Überladung gibt eine dynamische Methode (mit oder ohne JIT-Sichtbarkeitsprüfungen) aus, die die private PrivateMethod-Methode der Worker-Klasse aufruft. Die zweite Überladung gibt eine dynamische Methode aus, die auf eine internal-Eigenschaft (Friend-Eigenschaft in Visual Basic) der String-Klasse zugreift.

In dem Beispiel wird mit einer Hilfsmethode ein Berechtigungssatz erstellt, der auf die Internet-Berechtigungen begrenzt ist. Anschließend wird eine Anwendungsdomäne erstellt, wobei mit der AppDomain.CreateDomain(String, Evidence, AppDomainSetup, PermissionSet, StrongName[])-Methodenüberladung angegeben wird, dass der gesamte Code, der in der Domäne ausgeführt wird, diesen Berechtigungssatz verwendet. Außerdem wird eine Instanz der Worker-Klasse in der Anwendungsdomäne erstellt und die AccessPrivateMethod-Methode zweimal ausgeführt.

  • Bei der ersten Ausführung der AccessPrivateMethod-Methode werden JIT-Sichtbarkeitsprüfungen erzwungen. Die dynamische Methode schlägt fehl, wenn sie aufgerufen wird, da sie durch JIT-Sichtbarkeitsprüfungen daran gehindert wird, auf die private Methode zuzugreifen.

  • Bei der zweiten Ausführung der AccessPrivateMethod-Methode werden JIT-Sichtbarkeitsprüfungen übersprungen. Die dynamische Methode schlägt fehl, wenn sie kompiliert wird, da der Internet-Berechtigungssatz keine ausreichenden Berechtigungen zum Überspringen von Sichtbarkeitsprüfungen bereitstellt.

In diesem Beispiel wird dem Berechtigungssatz ReflectionPermission mit ReflectionPermissionFlag.RestrictedMemberAccess hinzugefügt. Anschließend wird eine zweite Domäne erstellt, wobei dem gesamten Code, der in der Domäne ausgeführt wird, die Berechtigungen im neuen Berechtigungssatz erteilt werden. Zusätzlich wird eine Instanz der Worker-Klasse in der neuen Anwendungsdomäne erstellt, und es werden beide Überladungen der AccessPrivateMethod-Methode ausgeführt.

  • Die erste Überladung der AccessPrivateMethod-Methode wird ausgeführt, und JIT-Sichtbarkeitsprüfungen werden übersprungen. Die dynamische Methode wird erfolgreich kompiliert und ausgeführt, da die Assembly, die den Code ausgibt, mit der Assembly identisch ist, die die private Methode enthält. Daher sind die Vertrauensebenen gleich. Wenn die Anwendung, die die Worker-Klasse enthält, über mehrere Assemblys verfügen würde, wäre der gleiche Prozess für jede dieser Assemblys erfolgreich, da sie alle die gleiche Vertrauensebene aufweisen würden.

  • Die zweite Überladung der AccessPrivateMethod-Methode wird ausgeführt, und JIT-Sichtbarkeitsprüfungen werden erneut übersprungen. Dieses Mal schlägt die dynamische Methode beim Kompilieren fehl, da sie versucht, auf die internalFirstChar-Eigenschaft der String-Klasse zuzugreifen. Die Assembly, die die String-Klasse enthält, ist voll vertrauenswürdig. Somit weist sie eine höhere Vertrauensebene auf als die Assembly, die den Code ausgibt.

Dieser Vergleich zeigt, wie teilweise vertrauenswürdiger Code durch ReflectionPermissionFlag.RestrictedMemberAccess Sichtbarkeitsprüfungen für anderen teilweise vertrauenswürdigen Code überspringen kann, ohne die Sicherheit von vertrauenswürdigem Code zu gefährden.

Code

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

Kompilieren des Codes

  • Wenn Sie dieses Codebeispiel in Visual Studio verwenden, müssen Sie den Namespace in den Namen der Klasse einbeziehen, wenn Sie diese an die CreateInstanceAndUnwrap-Methode übergeben. Standardmäßig ist der Namespace der Name des Projekts. Wenn das Projekt z. B. "PartialTrust" heißt, muss der Klassenname "PartialTrust.Worker" lauten.

Siehe auch