• XSS.stack #1 – первый литературный журнал от юзеров форума

Статья I/O Rings - Когда одной I/O операции не достаточно

Azrv3l

win32kfull
Эксперт
Регистрация
30.03.2019
Сообщения
215
Реакции
539
Введение
Обычно я пишу о функциях или методах безопасности в Windows. Но сегодняшний пост не имеет прямого отношения к безопасности, кроме обычного дополнительного риска, связанного с любым новым системным вызовом. Тем не менее, это интересное дополнение к миру I/O в Windows, которое может быть полезно для разработчиков, и я подумал, что было бы интересно изучить его и написать о нем. Все это говорит о том, что если вы ищете новый эксплойт или метод обхода EDR, вам следует сэкономить время и вместо этого просмотреть другие сообщения на этом веб-сайте.

Для тех троих, кто все еще читает, давайте поговорим о I/O rings!

I/O ring — это новая функция в preview сборках Windows. Это реализация кольцевого буфера в Windows — циклический буфер, в данном случае используемый для одновременной постановки в очередь нескольких операций ввода-вывода, чтобы позволить приложениям пользовательского режима, выполняющим множество операций ввода-вывода, делать это за одно действие вместо перехода от user к kernel и обратно для каждого отдельного запроса.

Этот новый функционал добавляет множество новых функций и внутренних структур данных, поэтому, чтобы не прерывать текст поста новыми структурами данных, я не буду помещать их в пост, их определения будут в примере кода в конце. Я покажу только несколько внутренних структур данных, которые не используются в примере кода.

Использование I/O Ring
Текущая реализация колец ввода-вывода поддерживает только операции чтения и позволяет ставить в очередь до 0x10000 операций за раз. Для каждой операции вызывающая сторона должна предоставить дескриптор целевого файла, буфер вывода, смещение в файле и объем памяти для чтения. Все это делается при помощи нескольких новых структурах данных, которые будут обсуждаться нижу. Но сначала вызывающая сторона должна инициализировать свое кольцо ввода-вывода.

Инициализация I/O Ring
Для этого система предоставляет новый системный вызов — NtCreateIoRing. Эта функция создает экземпляр нового типа объекта IoRing, описанного здесь как IORING_OBJECT:
C++:
typedef struct _IORING_OBJECT
{
  USHORT Type;
  USHORT Size;
  NT_IORING_INFO Info;
  PSECTION SectionObject;
  PVOID KernelMappedBase;
  PMDL Mdl;
  PVOID MdlMappedBase;
  ULONG_PTR ViewSize;
  ULONG SubmitInProgress;
  PVOID IoRingEntryLock;
  PVOID EntriesCompleted;
  PVOID EntriesSubmitted;
  KEVENT RingEvent;
  PVOID EntriesPending;
  ULONG BuffersRegistered;
  PIORING_BUFFER_INFO BufferArray;
  ULONG FilesRegistered;
  PHANDLE FileHandleArray;
} IORING_OBJECT, *PIORING_OBJECT;

NtCreateIoRing получает в качестве входного аргумента одну новую структуру — IO_RING_STRUCTV1. Эта структура содержит информацию о текущей версии, которая в настоящее время может быть только 1, обязательные и рекомендуемые флаги (оба в настоящее время не поддерживают никаких значений, кроме 0), а также запрошенный размер очереди отправки и очереди завершения.

Функция получает эту информацию и выполняет следующие действия:
  1. Проверяет все входные и выходные аргументы — их адреса, выравнивание размера и т. д.
  2. Проверяет запрошенный размер очереди отправки и вычисляет объем памяти, необходимый для очереди отправки, исходя из запрошенного количества записей.
    1. Если SubmissionQueueSize превышает 0x10000, возвращается новый статус ошибки STATUS_IORING_SUBMISSION_QUEUE_TOO_BIG.
  3. Проверяет размер очереди завершения и вычисляет объем памяти, необходимый для этого.
    1. Очередь завершения ограничена 0x20000 записями, и код ошибки STATUS_IORING_COMPLETION_QUEUE_TOO_BIG возвращается, если запрашивается большее число.
  4. Создает новый объект типа IoRingObjectType и инициализирует все поля, которые могут быть инициализированы в этот момент — флаги, размер и маску очереди отправки, событие и т.д.
  5. Создает раздел для очередей, отображает его в системном пространстве и создает для него MDL. Затем сопоставляет тот же раздел в пользовательском пространстве. Этот раздел будет содержать пространство отправки и пространство завершения, а так же будет использоваться приложением для передачи параметров для всех запрошенных операций ввода-вывода с ядром и получения кодов состояния.
  6. Инициализирует структуру вывода с адресом очереди отправки и другими данными, которые должны быть возвращены вызывающей стороне.
После успешного завершения NtCreateIoRing вызывающая сторона может записать свои данные в предоставленную очередь отправки. Очередь будет иметь заголовок очереди, за которым следует массив структур NT_IORING_SQE, каждая из которых представляет одну запрошенную операцию ввода-вывода. Заголовок описывает, какие записи должны быть обработаны в данный момент:
1.png


Заголовок очереди описывает, какие записи должны быть обработаны с использованием полей Head и Tail. Head указывает индекс последней необработанной записи, а Tail указывает индекс, на котором следует остановить обработку. Tail — Head должен быть меньше общего количества записей, а также быть равным или превышать количество записей, которые будут запрошены при вызове NtSubmitIoRing.

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

Коды операций I/O Ring
Есть четыре возможных типа операций, которые могут быть запрошены вызывающей стороной:
  1. IORING_OP_READ: запрашивает, чтобы система считывала данные из файла в выходной буфер. Дескриптор файла будет считан из поля FileRef в записи очереди отправки. Это будет интерпретироваться либо как дескриптор файла, либо как индекс в предварительно зарегистрированном массиве дескрипторов файлов, в зависимости от того, установлен ли флаг IORING_SQE_PREREGISTERED_FILE(1) в поле Flags записи очереди. Вывод будет записан в выходной буфер, указанный в поле Buffer записи. Подобно FileRef, это поле может вместо этого содержать индекс в предварительно зарегистрированном массиве выходных буферов, если установлен флаг IORING_SQE_PREREGISTERED_BUFFER (2).
  2. IORING_OP_REGISTERED_FILES: запрашивает предварительную регистрацию дескрипторов файлов для последующей обработки. В этом случае поле Buffer элемента очереди указывает на массив файловых дескрипторов. Запрошенные дескрипторы файлов будут продублированы и помещены в новый массив в поле FileHandleArray записи очереди. Поле FilesRegistered будет содержать количество дескрипторов файлов.
  3. IORING_OP_REGISTERED_BUFFERS: запрашивает предварительную регистрацию выходных буферов для считывания файловых данных. При этом поле Buffer в записи должно содержать массив структур IORING_BUFFER_INFO, описывающих адреса и размеры буферов, в которые будут считываться данные файла:
    Код:
    typedef struct _IORING_BUFFER_INFO{
        PVOID Address;
        ULONG Length;
    } IORING_BUFFER_INFO, *PIORING_BUFFER_INFO;
    Адреса и размеры буферов будут скопированы в новый массив и помещены в поле BufferArray очереди отправки. Поле BuffersRegistered будет содержать количество буферов.
  4. IORING_OP_CANCEL: запрашивает отмену ожидающей операции для файла. Как и в IORING_OP_READ, FileRef может быть дескриптором или индексом в массиве дескрипторов файлов в зависимости от флагов. В этом случае поле Buffer указывает на IO_STATUS_BLOCK, который необходимо отменить для файла.
Все эти параметры могут немного сбивать с толку, поэтому вот иллюстрации для 4 различных сценариев чтения, основанные на запрошенных флагах:

Флаги равны 0, используя поле FileRef в качестве дескриптора файла и поле Buffer в качестве указателя на выходной буфер:
2.png


Флаг IORING_SQE_PREREGISTERED_FILE(1) запрашивается, поэтому FileRef обрабатывается как индекс в массиве предварительно зарегистрированных файловых дескрипторов, а Buffer является указателем на выходной буфер:
3.png


Запрошен флаг IORING_SQE_PREREGISTERED_BUFFER(2), поэтому FileRef является дескриптором файла, а Buffer обрабатывается как индекс в массиве предварительно зарегистрированных выходных буферов:
4.png


Оба флага IORING_SQE_PREREGISTERED_FILE и IORING_SQE_PREREGISTERED_BUFFER установлены, поэтому FileRef обрабатывается как индекс в предварительно зарегистрированном массиве дескрипторов файлов, а Buffer обрабатывается как индекс в предварительно зарегистрированном массиве буферов:
5.png


Отправка и обработка I/O Ring
Как только вызывающая сторона настроит все свои записи в очереди отправки, она может вызвать NtSubmitIoRing, чтобы отправить свои запросы в ядро для обработки в соответствии с запрошенными параметрами. Внутри NtSubmitIoRing перебирает все записи и вызывает IopProcessIoRingEntry, отправляя объект IoRing и текущую запись очереди. Запись обрабатывается в соответствии с указанным OpCode, а затем вызывает IopIoRingDispatchComplete для заполнения очереди завершения. Очередь завершения, как и очередь отправки, начинается с заголовка, содержащего начало и конец, за которыми следует массив записей. Каждая запись представляет собой структуру IORING_CQE — она имеет значение UserData из записи очереди отправки, а также статус и информацию из IO_STATUS_BLOCK для операции:
C++:
typedef struct _IORING_CQE
{
    UINT_PTR UserData;
    HRESULT ResultCode;
    ULONG_PTR Information;
} IORING_CQE, *PIORING_CQE;

После завершения всех запрошенных записей система устанавливает событие в IoRingObject->RingEvent. Пока не все записи завершены, система будет ожидать события, используя тайм-аут, полученный от вызывающей стороны, и проснется, когда все запросы будут выполнены, вызывая сигнал о событии, или когда истечет тайм-аут.

Поскольку может быть обработано несколько записей, статус, возвращаемый вызывающей стороне, будет либо статусом ошибки, указывающим на сбой при обработке записей, либо возвращаемым значением KeWaitForSingleObject. Коды состояния для отдельных операций можно найти в очереди завершения — так что не путайте получение кода STATUS_SUCCESS от NtSubmitIoRing с успешными операциями чтения!

Использование I/O Ring официальным способом
Как и другие системные вызовы, эти новые функции IoRing не документированы и не предназначены для непосредственного использования. Вместо этого KernelBase.dll предлагает удобные функции-оболочки, которые получают простые в использовании аргументы и внутренне обрабатывают все недокументированные функции и структуры данных, которые необходимо отправить ядру. Существуют функции для создания, запроса, отправки и закрытия IORing, а также вспомогательные функции для создания записей в очереди для четырех различных операций, которые обсуждались ранее.

CreateIoRing
CreateIoRing получает информацию о флагах и размерах очередей, а также внутренне вызывает NtCreateIoRing и возвращает дескриптор экземпляра IoRing:
C++:
HRESULT CreateIoRing (
    _In_ IORING_VERSION IoRingVersion,
    _In_ IORING_CREATE_FLAGS Flags,
    _In_ UINT32 SubmissionQueueSize,
    _In_ UINT32 CompletionQueueSize,
    _Out_ HIORING* Handle
);

Этот новый тип дескриптора на самом деле является указателем на недокументированную структуру, содержащую структуру, возвращенную из NtCreateIoRing, и другие данные, необходимые для управления этим экземпляром IoRing:
C++:
typedef struct _HIORING
{
    ULONG SqePending;
    ULONG SqeCount;
    HANDLE handle;
    IORING_INFO Info;
    ULONG IoRingKernelAcceptedVersion;
} HIORING, *PHIORING;

Все остальные функции IORing получат этот дескриптор в качестве своего первого аргумента.

После создания экземпляра IORing приложению необходимо создать записи очереди для всех запрошенных операций ввода-вывода. Поскольку внутренняя структура очередей и структуры записей очереди не задокументированы, KernelBase.dll экспортирует вспомогательные функции для их создания с использованием входных данных, предоставленных вызывающей стороной. Для этого есть четыре функции:
  1. BuildIoRingReadFile
  2. BuildIoRingRegisterBuffers
  3. BuildIoRingRegisterFileHandles
  4. BuildIoRingCancelRequest
Каждая функция create добавляет новую запись очереди в очередь отправки с требуемым кодом операции и данными. Их имена делают их цели довольно очевидными, но давайте пройдемся по ним один за другим просто для ясности:

BuildIoRingReadFile
C++:
HRESULT BuildIoRingReadFile (
    _In_ HIORING IoRing,
    _In_ IORING_HANDLE_REF FileRef,
    _In_ IORING_BUFFER_REF DataRef,
    _In_ ULONG NumberOfBytesToRead,
    _In_ ULONG64 FileOffset,
    _In_ ULONG_PTR UserData,
    _In_ IORING_SQE_FLAGS Flags
);

Функция получает дескриптор, возвращенный CreateIoRing, за которым следуют два указателя на новые структуры данных. Обе эти структуры имеют поле Kind, которое может иметь значение IORING_REF_RAW, указывающее, что предоставленное значение является необработанной ссылкой, или IORING_REF_REGISTERED, указывающее, что значение является индексом в предварительно зарегистрированном массиве. Второе поле представляет собой объединение значения и индекса, в котором будет указан дескриптор файла или буфер.

BuildIoRingRegisterFileHandles and BuildIoRingRegisterBuffers
C++:
HRESULT BuildIoRingRegisterFileHandles (
    _In_ HIORING IoRing,
    _In_ ULONG Count,
    _In_ HANDLE const Handles[],
    _In_ PVOID UserData
);

HRESULT BuildIoRingRegisterBuffers (
    _In_ HIORING IoRing,
    _In_ ULONG Count,
    _In_ IORING_BUFFER_INFO count Buffers[],
    _In_ PVOID UserData
);

Эти две функции создают записи очереди отправки для предварительной регистрации дескрипторов файлов и выходных буферов. Оба получают дескриптор, возвращенный из CreateIoRing, количество предварительно зарегистрированных файлов/буферов в массиве, массив дескрипторов или буферов для регистрации и пользовательские данные.

В BuildIoRingRegisterFileHandles Handles — это указатель на массив дескрипторов файлов, а в BuildIoRingRegisterBuffers — Buffers — это указатель на массив структур IORING_BUFFER_INFO, содержащих базу и размер Buffer.

BuildIoRingCancelRequest
C++:
HRESULT BuildIoRingCancelRequest (
    _In_ HIORING IoRing,
    _In_ IORING_HANDLE_REF File,
    _In_ PVOID OpToCancel,
    _In_ PVOID UserData
);

Как и другие функции, BuildIoRingCancelRequest получает в качестве первого аргумента дескриптор, возвращенный функцией CreateIoRing. Второй аргумент снова является указателем на структуру IORING_REQUEST_DATA, содержащую дескриптор (или индекс в массиве файловых дескрипторов) файла, операция которого должна быть отменена. Третий и четвертый аргументы — это буфер вывода и пользовательские данные, которые должны быть помещены в запись очереди.

После того, как все записи очереди были созданы с помощью этих функций, очередь может быть отправлена:

SubmitIoRing
C++:
HRESULT SubmitIoRing (
    _In_ HIORING IoRingHandle,
    _In_ ULONG WaitOperations,
    _In_ ULONG Milliseconds,
    _Out_ PULONG SubmittedEntries
);

Функция получает тот же дескриптор, что и первый аргумент, который использовался для инициализации IoRing и очереди отправки. Затем он получает количество записей для отправки, время в миллисекундах ожидания завершения операций и указатель на выходной параметр, который будет получать количество отправленных записей.

GetIoRingInfo

C++:
HRESULT GetIoRingInfo (
    _In_ HIORING IoRingHandle,
    _Out_ PIORING_INFO IoRingBasicInfo
);

Этот API возвращает информацию о текущем состоянии IoRing с новой структурой:
C++:
typedef struct _IORING_INFO
{
  IORING_VERSION IoRingVersion;
  IORING_CREATE_FLAGS Flags;
  ULONG SubmissionQueueSize;
  ULONG CompletionQueueSize;
} IORING_INFO, *PIORING_INFO;

Он содержит версию и флаги IoRing, а также текущий размер очередей отправки и завершения.

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

До сих пор я не мог найти в системе ничего, что бы использовало эту функцию, но после выпуска 21H2 я ожидаю, что приложения Windows с большим объемом ввода-вывода начнут использовать ее, вероятно, в основном в серверных и azure средах.

Итог
На данный момент общедоступной документации для этого нового дополнения к миру ввода-вывода в Windows не существует, но мы надеемся, что когда 21H2 будет выпущена позже в этом году, мы увидим, что все это официально задокументировано и используется как приложениями Windows, так и сторонними приложениями. При разумном использовании это может привести к значительному повышению производительности приложений с частыми операциями чтения. Как и каждая новая функция и системный вызов, это также может иметь неожиданные последствия для безопасности. Одну ошибку уже нашел hFiref0x, который первым публично упомянул об этой функции и сумел вывести систему из строя, отправив неверный параметр в NtCreateIoRing — ошибка, которая с тех пор была исправлена. Более пристальное изучение этих функций, вероятно, приведет к большему количеству таких открытий и интересных побочных эффектов этого нового механизма.

Код

Вот небольшой PoC, показывающий два способа использования колец ввода-вывода — либо через официальный API KernelBase, либо через внутренний API ntdll. Чтобы код скомпилировался правильно, обязательно свяжите его с файлом onecoreuap.lib (для функций KernelBase) или ntdll.lib (для функций ntdll):
C++:
#include <ntstatus.h>
#define WIN32_NO_STATUS
#include <Windows.h>
#include <cstdio>
#include <ioringapi.h>
#include <winternal.h>

typedef struct _IO_RING_STRUCTV1
{
    ULONG IoRingVersion;
    ULONG SubmissionQueueSize;
    ULONG CompletionQueueSize;
    ULONG RequiredFlags;
    ULONG AdvisoryFlags;
} IO_RING_STRUCTV1, *PIO_RING_STRUCTV1;

typedef struct _IORING_QUEUE_HEAD
{
    ULONG Head;
    ULONG Tail;
    ULONG64 Flags;
} IORING_QUEUE_HEAD, *PIORING_QUEUE_HEAD;

typedef struct _NT_IORING_INFO
{
    ULONG Version;
    IORING_CREATE_FLAGS Flags;
    ULONG SubmissionQueueSize;
    ULONG SubQueueSizeMask;
    ULONG CompletionQueueSize;
    ULONG CompQueueSizeMask;
    PIORING_QUEUE_HEAD SubQueueBase;
    PVOID CompQueueBase;
} NT_IORING_INFO, *PNT_IORING_INFO;

typedef struct _NT_IORING_SQE
{
    ULONG Opcode;
    ULONG Flags;
    HANDLE FileRef;
    LARGE_INTEGER FileOffset;
    PVOID Buffer;
    ULONG BufferSize;
    ULONG BufferOffset;
    ULONG Key;
    PVOID Unknown;
    PVOID UserData;
    PVOID stuff1;
    PVOID stuff2;
    PVOID stuff3;
    PVOID stuff4;
} NT_IORING_SQE, *PNT_IORING_SQE;

EXTERN_C_START
NTSTATUS
NtSubmitIoRing (
    _In_ HANDLE Handle,
    _In_ IORING_CREATE_REQUIRED_FLAGS Flags,
    _In_ ULONG EntryCount,
    _In_ PLARGE_INTEGER Timeout
    );

NTSTATUS
NtCreateIoRing (
    _Out_ PHANDLE pIoRingHandle,
    _In_ ULONG CreateParametersSize,
    _In_ PIO_RING_STRUCTV1 CreateParameters,
    _In_ ULONG OutputParametersSize,
    _Out_ PNT_IORING_INFO pRingInfo
    );

NTSTATUS
NtClose (
    _In_ HANDLE Handle
    );

EXTERN_C_END

void IoRingNt ()
{
    NTSTATUS status;
    IO_RING_STRUCTV1 ioringStruct;
    NT_IORING_INFO ioringInfo;
    HANDLE handle;
    PNT_IORING_SQE sqe;
    LARGE_INTEGER timeout;
    HANDLE hFile = NULL;
    ULONG sizeToRead = 0x200;
    PVOID *buffer = NULL;
    ULONG64 endOfBuffer;

    ioringStruct.IoRingVersion = 1;
    ioringStruct.SubmissionQueueSize = 1;
    ioringStruct.CompletionQueueSize = 1;
    ioringStruct.AdvisoryFlags = IORING_CREATE_ADVISORY_FLAGS_NONE;
    ioringStruct.RequiredFlags = IORING_CREATE_REQUIRED_FLAGS_NONE;

    status = NtCreateIoRing(&handle,
                            sizeof(ioringStruct),
                            &ioringStruct,
                            sizeof(ioringInfo),
                            &ioringInfo);
    if (!NT_SUCCESS(status))
    {
        printf("Failed creating IO ring handle: 0x%x\n", status);
        goto Exit;
    }

    ioringInfo.SubQueueBase->Tail = 1;
    ioringInfo.SubQueueBase->Head = 0;
    ioringInfo.SubQueueBase->Flags = 0;

    hFile = CreateFile(L"C:\\Windows\\System32\\notepad.exe",
                       GENERIC_READ,
                       0,
                       NULL,
                       OPEN_EXISTING,
                       FILE_ATTRIBUTE_NORMAL,
                       NULL);

    if (hFile == INVALID_HANDLE_VALUE)
    {
        printf("Failed opening file handle: 0x%x\n", GetLastError());
        goto Exit;
    }

    sqe = (PNT_IORING_SQE)((ULONG64)ioringInfo.SubQueueBase + sizeof(IORING_QUEUE_HEAD));
    sqe->Opcode = 1;
    sqe->Flags = 0;
    sqe->FileRef = hFile;
    sqe->FileOffset.QuadPart = 0;
    buffer = (PVOID*)VirtualAlloc(NULL, sizeToRead, MEM_COMMIT, PAGE_READWRITE);
    if (buffer == NULL)
    {
        printf("Failed allocating memory\n");
        goto Exit;
    }
    sqe->Buffer = buffer;
    sqe->BufferOffset = 0;
    sqe->BufferSize = sizeToRead;
    sqe->Key = 1234;
    sqe->UserData = nullptr;

    timeout.QuadPart = -10000;

    status = NtSubmitIoRing(handle, IORING_CREATE_REQUIRED_FLAGS_NONE, 1, &timeout);
    if (!NT_SUCCESS(status))
    {
        printf("Failed submitting IO ring: 0x%x\n", status);
        goto Exit;
    }
    printf("Data from file:\n");
    endOfBuffer = (ULONG64)buffer + sizeToRead;
    for (; (ULONG64)buffer < endOfBuffer; buffer++)
    {
        printf("%p ", *buffer);
    }
    printf("\n");

Exit:
    if (handle)
    {
        NtClose(handle);
    }
    if (hFile)
    {
        NtClose(hFile);
    }
    if (buffer)
    {
        VirtualFree(buffer, NULL, MEM_RELEASE);
    }
}

void IoRingKernelBase ()
{
    HRESULT result;
    HIORING handle;
    IORING_CREATE_FLAGS flags;
    IORING_HANDLE_REF requestDataFile;
    IORING_BUFFER_REF requestDataBuffer;
    UINT32 submittedEntries;
    HANDLE hFile = NULL;
    ULONG sizeToRead = 0x200;
    PVOID *buffer = NULL;
    ULONG64 endOfBuffer;

    flags.Required = IORING_CREATE_REQUIRED_FLAGS_NONE;
    flags.Advisory = IORING_CREATE_ADVISORY_FLAGS_NONE;
    result = CreateIoRing(IORING_VERSION_1, flags, 1, 1, &handle);
    if (!SUCCEEDED(result))
    {
        printf("Failed creating IO ring handle: 0x%x\n", result);
        goto Exit;
    }

    hFile = CreateFile(L"C:\\Windows\\System32\\notepad.exe",
                       GENERIC_READ,
                       0,
                       NULL,
                       OPEN_EXISTING,
                       FILE_ATTRIBUTE_NORMAL,
                       NULL);
    if (hFile == INVALID_HANDLE_VALUE)
    {
        printf("Failed opening file handle: 0x%x\n", GetLastError());
        goto Exit;
    }
    requestDataFile.Kind = IORING_REF_RAW;
    requestDataFile.Handle = hFile;
    requestDataBuffer.Kind = IORING_REF_RAW;
    buffer = (PVOID*)VirtualAlloc(NULL,
                                  sizeToRead,
                                  MEM_COMMIT,
                                  PAGE_READWRITE);
    if (buffer == NULL)
    {
        printf("Failed to allocate memory\n");
        goto Exit;
    }
    requestDataBuffer.Buffer = buffer;
    result = BuildIoRingReadFile(handle,
                                 requestDataFile,
                                 requestDataBuffer,
                                 sizeToRead,
                                 0,
                                 NULL,
                                 IOSQE_FLAGS_NONE);
    if (!SUCCEEDED(result))
    {
        printf("Failed building IO ring read file structure: 0x%x\n", result);
        goto Exit;
    }

    result = SubmitIoRing(handle, 1, 10000, &submittedEntries);
    if (!SUCCEEDED(result))
    {
        printf("Failed submitting IO ring: 0x%x\n", result);
        goto Exit;
    }
    printf("Data from file:\n");
    endOfBuffer = (ULONG64)buffer + sizeToRead;
    for (; (ULONG64)buffer < endOfBuffer; buffer++)
    {
        printf("%p ", *buffer);
    }
    printf("\n");

Exit:
    if (handle != 0)
    {
        CloseIoRing(handle);
    }
    if (hFile)
    {
        NtClose(hFile);
    }
    if (buffer)
    {
        VirtualFree(buffer, NULL, MEM_RELEASE);
    }
}

int main ()
{
    IoRingKernelBase();
    IoRingNt();
    ExitProcess(0);
}

Ссылки


От ТС
Эта статья является переводом, оригинал доступен тут
Приведённое описание механизма I/O Ring было выпущенно в мае 2021, с тех пор механизм изменился. Позже сделаю перевод статьи с анализом более актуальной версии - https://windows-internals.com/one-year-to-i-o-ring-what-changed/
txOrigin уже переводил статью о RW примитиве на основе I/O Ring, из блога Yarden Shafir - https://xss.pro/threads/71217/

Перевод:
Azrv3l cпециально для xss.pro
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх