Поделиться через


Пошаговое руководство: Генерация кода в сценариях частичного доверия

Отражение использует тот же набор API при полном или частичном доверии, но для определённых функций требуются особые разрешения в коде с частичным доверием. Кроме того, отражение содержит функцию, а именно, анонимно размещаемые динамические методы, предназначенные для использования с частичным доверием и сборками, прозрачными для безопасности.

Примечание.

Перед .NET Framework 3.5 для генерации кода требовался флаг ReflectionPermission с ReflectionPermissionFlag.ReflectionEmit. Это разрешение включается по умолчанию в наборы именованных разрешений FullTrust и Intranet, но не в наборе разрешений Internet. Таким образом, библиотеку можно использовать из частичного доверия только в том случае, если у неё есть атрибут SecurityCriticalAttribute и также выполняется метод Assert для ReflectionEmit. Для таких библиотек требуется тщательная проверка безопасности, так как ошибки кодирования могут привести к ошибкам безопасности. Платформа .NET Framework 3.5 позволяет создавать код в сценариях частичного доверия без выдачи требований безопасности, так как создание кода по сути не является привилегированной операцией. То есть созданный код не имеет больше разрешений, чем сборка, которая выдает его. Это позволяет библиотекам, которые генерируют код, быть прозрачными в плане безопасности и устраняет необходимость утверждения ReflectionEmit, чтобы написание защищенной библиотеки не требовало такой всесторонней проверки безопасности.

В этом пошаговом руководстве рассматриваются следующие задачи:

Для получения дополнительной информации о выпуске кода в сценариях частичного доверия см. Проблемы безопасности в отражении Emit.

Полный список кода, показанного в этих процедурах, см. в разделе "Пример " в конце этого пошагового руководства.

Настройка частично доверенных расположений

В следующих двух процедурах показано, как настроить расположения, из которых можно выполнять тестирование кода с частичным доверием.

  • В первой процедуре показано, как создать изолированный домен приложения, которому предоставлены права на использование Интернета.

  • Вторая процедура показывает, как добавить ReflectionPermission с флагом ReflectionPermissionFlag.RestrictedMemberAccess в домен частично доверенного приложения, чтобы обеспечить доступ к закрытым данным в сборках того же или меньшего уровня доверия.

Создание изолированных доменов приложений

Чтобы создать домен приложения, где сборки выполняются с частичным доверием, необходимо указать набор разрешений, которые будут предоставлены сборкам, используя AppDomain.CreateDomain(String, Evidence, AppDomainSetup, PermissionSet, StrongName[]) перегрузку метода для создания домена приложения. Самый простой способ указать набор разрешений — получить именованный набор разрешений из политики безопасности.

Следующая процедура создает песочницу для домена приложения, который запускает ваш код с ограниченным доверием, для тестирования сценариев, в которых генерируемый код может получить доступ только к общедоступным членам общедоступных типов. В следующей процедуре показано, как добавить RestrictedMemberAccess, чтобы протестировать сценарии, в которых создаваемый код может получить доступ к непубличным типам и членам в сборках, имеющим равные или меньшие разрешения.

Чтобы создать домен приложения с частичным доверием

  1. Создайте набор разрешений для предоставления сборкам в изолированном домене приложения. В этом случае используется набор разрешений зоны Интернета.

    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. AppDomainSetup Создайте объект для инициализации домена приложения с помощью пути приложения.

    Это важно

    Для простоты в этом примере кода используется текущая папка. Чтобы запустить код, который фактически поставляется из Интернета, используйте отдельную папку для ненадежного кода, как описано в разделе "Практическое руководство. Запуск частично доверенного кода в песочнице".

    AppDomainSetup adSetup = new AppDomainSetup();
    adSetup.ApplicationBase = ".";
    
    Dim adSetup As New AppDomainSetup()
    adSetup.ApplicationBase = "."
    
  3. Создайте домен приложения, задавая сведения о настройке домена приложения и набор разрешений для всех сборок, выполняемых в домене приложения.

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

    Последний параметр перегрузки AppDomain.CreateDomain(String, Evidence, AppDomainSetup, PermissionSet, StrongName[]) метода позволяет указать набор сборок, которые получают полное доверие, а не набор разрешений домена приложения. Вам не нужно указывать сборки .NET Framework, которые использует приложение, так как эти сборки находятся в глобальном кэше сборок. Сборки в глобальном кэше сборок всегда полностью доверенны. Этот параметр можно использовать для указания сборок с строгим именем, которые не находятся в глобальном кэше сборок.

Добавление RestrictedMemberAccess в изолированные домены

Ведущие приложения могут разрешить анонимным динамическим методам иметь доступ к частным данным в сборках с уровнями доверия, равными или меньше уровня доверия сборки, которая выдает код. Чтобы включить эту ограниченную возможность пропуска проверки видимости JIT, хост-приложение добавляет объект ReflectionPermission с флагом ReflectionPermissionFlag.RestrictedMemberAccess (RMA) в набор разрешений.

Например, хост может предоставлять интернет-приложениям разрешения на доступ к Интернету, а также RMA, чтобы интернет-приложение могло создавать код, который имеет доступ к конфиденциальным данным в своих собственных сборках. Так как доступ ограничен сборками равного или меньшего доверия, интернет-приложение не может получить доступ к членам полностью доверенных сборок, таких как сборки .NET Framework.

Примечание.

Чтобы предотвратить повышение привилегий, сведения о стеке для выдающей сборки включаются, когда создаются анонимно размещаемые динамические методы. При вызове метода проверяется информация о стеке. Таким образом, анонимный динамический метод, вызываемый из полностью доверенного кода, по-прежнему ограничен уровнем доверия выдающей сборки.

Создание домена приложения с частичным доверием и RMA

  1. Создайте новый объект ReflectionPermission с флагом RMA (RestrictedMemberAccess) и используйте метод PermissionSet.SetPermission, чтобы добавить разрешение в набор предоставления.

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

    Метод AddPermission добавляет разрешение в набор разрешений, если оно еще не включено. Если разрешение уже включено в набор разрешений, указанные флаги добавляются в существующее разрешение.

    Примечание.

    RMA — это функция анонимно размещенных динамических методов. Если обычные динамические методы пропускают проверки видимости JIT, сгенерированный код требует полного доверия.

  2. Создайте домен приложения, указав информацию по настройке домена приложения и набор разрешений.

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

Выполнение кода в изолированных доменах приложений

В следующей процедуре объясняется, как определить класс с помощью методов, которые могут выполняться в домене приложения, как создать экземпляр класса в домене и как выполнить его методы.

Определение и выполнение метода в домене приложения

  1. Определите класс, производный от MarshalByRefObject. Это позволяет создавать экземпляры класса в других доменах приложений и выполнять вызовы методов между границами домена приложения. Класс в этом примере называется Worker.

    public class Worker : MarshalByRefObject
    {
    
    Public Class Worker
        Inherits MarshalByRefObject
    
  2. Определите открытый метод, содержащий код, который требуется выполнить. В этом примере код генерирует простой динамический метод, создает делегат для выполнения этого метода, а затем вызывает делегат.

    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. В вашей главной программе получите отображаемое имя вашей сборки. Это имя используется при создании экземпляров Worker класса в изолированном домене приложения.

    String asmName = typeof(Worker).Assembly.FullName;
    
    Dim asmName As String = GetType(Worker).Assembly.FullName
    
  4. В основной программе создайте изолированный домен приложения, как описано в первой процедуре в этом пошаговом руководстве. Вам не нужно добавлять разрешения в Internet набор разрешений, так как метод SimpleEmitDemo использует только открытые методы.

  5. В главной программе создайте экземпляр Worker класса в изолированном домене приложения.

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

    Метод CreateInstanceAndUnwrap создает объект в домене целевого приложения и возвращает прокси-сервер, который можно использовать для вызова свойств и методов объекта.

    Примечание.

    Если этот код используется в Visual Studio, необходимо изменить имя класса, чтобы включить пространство имен. По умолчанию пространство имен — это имя проекта. Например, если проект имеет значение "PartialTrust", то имя класса должно быть "PartialTrust.Worker".

  6. Добавьте код для вызова SimpleEmitDemo метода. Вызов переносится через границу домена приложения, и код выполняется в песочном домене приложения.

    w.SimpleEmitDemo();
    
    w.SimpleEmitDemo()
    

Использование анонимных динамических методов

Анонимно размещенные динамические методы связаны с прозрачной сборкой, предоставляемой системой. Поэтому содержащийся код является прозрачным. С другой стороны, обычные динамические методы должны быть связаны с существующим модулем (непосредственно указанным или выведенным из связанного типа) и принимать уровень безопасности из этого модуля.

Примечание.

Единственный способ связать динамический метод с сборкой, предоставляющей анонимное размещение, — использовать конструкторы, описанные в следующей процедуре. Невозможно явно указать модуль в сборке анонимного размещения.

Обычные динамические методы имеют доступ к внутренним членам модуля, с которыми они связаны, или к частным членам типа, с которыми они связаны. Так как анонимные динамические методы изолированы от другого кода, у них нет доступа к частным данным. Однако у них есть ограниченная возможность пропуска проверок видимости JIT для получения доступа к частным данным. Эта возможность ограничена сборками, которые имеют уровни доверия, равные или меньше уровня доверия сборки, которая выдает код.

Чтобы предотвратить повышение привилегий, сведения о стеке для выдающей сборки включаются, когда создаются анонимно размещаемые динамические методы. При вызове метода проверяется информация о стеке. Анонимный динамический метод, вызываемый из полностью доверенного кода, по-прежнему ограничен уровнем доверия сборки, которая ее вызвала.

Использование анонимных динамических методов

  • Создайте анонимный динамический метод с помощью конструктора, который не указывает связанный модуль или тип.

    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)
    

    Если анонимный динамический метод использует только общедоступные типы и методы, он не требует ограниченного доступа к членам и не должен пропускать проверки видимости JIT.

    Специальные разрешения не требуются для создания динамического метода, но в сгенерированном коде необходимы разрешения, которые предъявляются типами и методами, которые он использует. Например, если создаваемый код вызывает метод, который обращается к файлу, требуется FileIOPermission. Если уровень доверия не включает это разрешение, при выполнении генерируемого кода возникает исключение безопасности. Приведенный здесь код генерирует динамический метод, который использует только метод Console.WriteLine. Поэтому код можно выполнить из частично доверенных расположений.

  • Кроме того, создайте анонимно размещённый динамический метод с ограниченной возможностью обхода проверок видимости JIT, воспользовавшись конструктором DynamicMethod(String, Type, Type[], Boolean) и указав true для параметра restrictedSkipVisibility.

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

    Ограничение заключается в том, что анонимный динамический метод может получить доступ к частным данным только в сборках с уровнями доверия, равными или меньше уровня доверия издаваемой сборки. Например, если динамический метод выполняется с доверием к Интернету, он может получить доступ к частным данным в других сборках, которые также выполняются с доверием к Интернету, но он не может получить доступ к частным данным сборок .NET Framework. Сборки .NET Framework устанавливаются в глобальный кэш сборок и всегда полностью доверенны.

    Анонимные динамические методы могут использовать эту ограниченную возможность обхода проверки видимости JIT, только если хост-приложение предоставляет ReflectionPermission с флагом ReflectionPermissionFlag.RestrictedMemberAccess. Запрос на это разрешение производится при вызове метода.

    Примечание.

    Сведения о стеке вызовов для сборки, создающей динамический метод, включаются при его создании. Поэтому запрос выполняется в адрес разрешений испускающей сборки, а не сборки, которая вызывает метод. Это предотвращает выполнение исходящего кода с повышенными полномочиями.

    Полный пример кода в конце этого пошагового руководства демонстрирует использование и ограничения ограниченного доступа к членам. Его Worker класс включает метод, который может создавать анонимные динамические методы с ограниченными возможностями пропуска проверок видимости, а в примере показан результат выполнения этого метода в доменах приложений с разными уровнями доверия.

    Примечание.

    Ограниченная возможность пропуска проверок видимости — это функция анонимно размещенных динамических методов. Если обычные динамические методы пропускают проверки видимости JIT, им должно быть полное доверие.

Пример

Описание

В следующем примере кода показано использование флага RestrictedMemberAccess, чтобы разрешить анонимно размещенным динамическим методам пропускать проверки видимости JIT, но только если уровень доверия целевого элемента равен или ниже уровня доверия сборки, которая выполняет генерацию кода.

В примере определяется класс Worker, который можно маршалировать между границами областей приложения. Класс имеет две AccessPrivateMethod перегрузки метода, которые создают и выполняют динамические методы. Первая перегрузка создаёт динамический метод, который вызывает приватный PrivateMethod метод Worker класса, и он может генерировать динамический метод с проверкой видимости JIT или без неё. Вторая перегрузка создает динамический метод, который обращается к свойству internal (свойству Friend в Visual Basic) класса String.

В примере используется вспомогательный метод для создания ограниченного грантового набора Internet, а затем создается домен приложения с использованием перегруженного метода AppDomain.CreateDomain(String, Evidence, AppDomainSetup, PermissionSet, StrongName[]), чтобы указать, что весь код, который выполняется в этом домене, использует этот грантовый набор. В примере создается экземпляр Worker класса в домене приложения и выполняется AccessPrivateMethod метод два раза.

  • При первом выполнении метода AccessPrivateMethod видимость JIT проверяется. Динамический метод завершается ошибкой при вызове, так как проверки видимости JIT не позволяют получить доступ к закрытому методу.

  • Во второй раз, когда метод AccessPrivateMethod запускается, проверки видимости JIT обходятся. Динамический метод завершается ошибкой при компиляции, так как Internet набор разрешений не предоставляет достаточных разрешений для пропуска проверок видимости.

Пример добавляет ReflectionPermission и ReflectionPermissionFlag.RestrictedMemberAccess в набор грантов. Затем в примере создается второй домен, указывая, что весь код, выполняемый в домене, получает разрешения из нового набора разрешений. В примере создается экземпляр класса Worker в новом домене приложения, и выполняются обе перегрузки метода AccessPrivateMethod.

  • Выполняется первая перегрузка метода AccessPrivateMethod, а проверки видимости JIT пропускаются. Динамический метод компилируется и выполняется успешно, так как сборка, которая выдает код, совпадает с сборкой, содержащей закрытый метод. Поэтому уровни доверия равны. Если приложение, содержащее Worker класс, имеет несколько сборок, то один и тот же процесс будет успешным для любой из этих сборок, так как все они будут находиться на одном уровне доверия.

  • Выполняется вторая перегрузка метода AccessPrivateMethod, и проверки видимости JIT снова пропускаются. На этот раз динамический метод завершается ошибкой при компиляции, так как он пытается получить доступ к internalFirstChar свойству String класса. Сборка, содержащая этот класс String, полностью доверяется. Поэтому он находится на более высоком уровне доверия, чем сборка, которая генерирует код.

В этом сравнении показано, как ReflectionPermissionFlag.RestrictedMemberAccess позволяет частично доверенному коду пропускать проверки видимости для другого частично доверенного кода без ущерба для безопасности доверенного кода.

Код

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

Компиляция кода

  • Если вы создаете этот пример кода в Visual Studio, необходимо изменить имя класса, чтобы включить пространство имен при передаче его в CreateInstanceAndUnwrap метод. По умолчанию пространство имен — это имя проекта. Например, если проект имеет значение "PartialTrust", то имя класса должно быть "PartialTrust.Worker".

См. также