Русский
Русский
English
Статистика
Реклама

Ntfs

Утраченный потенциал подсистемы Windows для Linux (WSL)

06.01.2021 10:12:06 | Автор: admin


Если вы несколько лет вообще не следили за Windows 10 и не знаете, что происходит, то пропустили одну вещь очень горячей темой для разработчиков стала подсистема Windows для Linux, она же WSL. Среди программистов очень часто её обсуждают. Действительно, потрясающе интересная штука.

Наконец-то у нас появилась возможность запустить свой инструментарий Linux на Windows наравне с виндовыми программами. А это значит, что больше не нужно изучать странный PowerShell или пользоваться архаичной консолью CMD.EXE.

К сожалению, не всё так радужно. WSL по-прежнему является неким инородным элементом, который отделён от родной среды Windows. В частности, не может взаимодействовать с родными инструментами Windows.

А ведь изначально всё задумывалось совсем иначе, пишет Джулио Мерино (Julio Merino), автор блога для разработчиков jmmv.dev. Подсистема должна была стать совсем другой, но фактически вышел провал, в каком-то смысле.

Чтобы понять причины этого провала, нужно сначала понять различия между WSL 1 и WSL 2 и как переход на WSL 2 закрыл некоторые интересные перспективы.

Обзор архитектуры WSL 1


Давайте сначала взглянем на WSL 1, и первым делом на её странное название. Почему эта функция называется подсистемой Windows для Linux? Разве не наоборот? Это же не подсистема в Linux, которая делает что-то связанное с Windows, а именно наоборот! То есть грамотно она должна называться Подсистема с функциональностью Linux для Windows или Подсистема Linux для Windows или LSW. Откуда путаница?

Есть мнение, что Microsoft была вынуждена выбрать название наоборот, чтобы избежать упоминания чужих торговых марок. Вот слова Рича Тёрнера (Rich Turner), проект-менеджера WSL:


Что ж с другой стороны, такое странное название технически можно считать корректным, если внимательно изучить архитектуру ядра Windows NT. На странице Архитектура Windows NT в Википедии мы находим следующее:



Пользовательский режим Windows NT состоит из подсистем, передающих запросы ввода-вывода соответствующему драйверу режима ядра посредством менеджера ввода-вывода. Есть две подсистемы на уровне пользователя: подсистемы окружения (запускают приложения, написанные для разных операционных систем) и интегрированные или внутренние подсистемы (управляет особыми системными функциями от имени подсистемы окружения). Режим ядра имеет полный доступ к аппаратной части и системным ресурсам компьютера.

Windows NT разработана с нуля для поддержки процессов, запущенных из множества операционных систем, а Win32 был просто одной из этих подсистем окружения. На такой платформе WSL 1 предоставляет новую подсистему окружения, подсистему Linux, для запуска бинарников Linux поверх ядра Windows NT. У подсистем окружения Win32 и Linux должна быть одна общая интегральная подсистема.

Что всё это на самом деле значит?

Разные системы называют фронтендами вот что это значит. Процесс пользовательского пространства это набор двоичных команд, которые процессор выполняет непрерывно (оставим в стороне прерывания). Ядро операционной системы не знает о том, что делает процесс, пока процесс не выдаст системный вызов: в этот момент ядро восстанавливает контроль для выполнения операции от имени пользователя, которая может быть чем-то вроде чтения файла или паузы на несколько секунд.

Способ, которым процесс выполняет системные вызовы, и семантика этих системных вызовов специфичны для операционной системы. Например, на старых х86 это выглядит так: открытие файла в системе Win32 системный вызов 17h, который инициируется через прерывание INT 2EH, а при открытии файла в системе Linux это системный вызов 5h, который инициируется через прерывание INT 80х.

Но концептуально открытие файла это открытие файла, верно? Нам особенно не интересно, что номера системных вызовов или номера прерываний отличаются друг от друга. И в этом заключается ключевой аспект дизайна WSL 1: подсистема Linux в ядре NT это, проще говоря, реализация уровня системных вызовов Linux перед ядром NT. Эти системные вызовы позже делегируются примитивам NT, а не вызовам Win32. Самое главное: нет никакого перевода с системных вызовов Linux на системные вызовы Win32.

В каком-то смысле это подвиг архитектурной мысли и реальной программной разработки, учитывая в целом хорошую поддержку Linux-приложений под WSL 1 и помня о множестве реальных внутренних отличий NT от Unix, когда Windows вообще нативно не воспринимает стандартную юниксовую логику forkexec.

Истинная красота этой конструкции заключается в том, что на машине работает одно ядро, и это ядро имеет целостное представление обо всех процессах под ним. Ядро знает всё о процессах Win32 и Linux. И все эти процессы взаимодействуют с общими ресурсами, такими как единый сетевой стек, единый менеджер памяти и единый планировщик процессов.

Причины для создания WSL 2


Если WSL 1 так крут, то зачем нужен WSL 2? Здесь две причины:

  • WSL 1 должен, по сути, реализовать все двоичные интерфейсы приложений (ABI) ядра Linux, как говорится, бит к биту. Если в интерфейсе есть ошибка, WSL 1 должен её воспроизвести. А если есть функция, которую трудно представить в ядре NT, то она либо не может быть реализована, либо нуждается в дополнительной логике ядра (и поэтому становится медленнее).


    Уровни и интерфейсы между ними: API и ABI. Высокоуровневое сравнение

  • Подсистема Linux в WSL 1 должна соблюдать любые ограничения и внутренние различия, существующие между ядром NT и традиционным дизайном Unix. Наиболее очевидным отличием является файловая система NTFS и её семантика, а также то, как эти различия вредят производительности бинарных файлов Linux. Низкая производительность файловой системы, видимо, была распространённой жалобой при использовании WSL 1.

WSL 2 выбрасывает всю часть подсистемы Linux и заменяет её полноценной (но очень хорошо скрытой и быстрой) виртуальной машиной. Затем виртуальная машина внутри себя запускает обычное ядро Linux, правильную файловую систему Linux и стандартный сетевой стек Linux. Всё это работает внутри VM.

Это означает, что красота дизайна WSL 1 исчезла: ядро Windows NT больше не видит ничего в мире Linux. Просто большой чёрный ящик, который делает что-то неизвестное внутри себя. Ядро Windows NT видит только точки подключения VMENTER и VMEXIT для виртуальной машины и запросы чтения/записи на уровне блоков на виртуальном диске. Это самое ядро NT теперь ничего не знает о процессах Linux и доступе к файлам. Точно так же работающее ядро Linux ничего не знает об NT.

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

Потерянный потенциал


С точки зрения пользователя, подсистема WSL 2 выглядит лучше: действительно, приложения Linux теперь работают намного быстрее, потому что не проходят через неудобную эмуляцию системных вызовов Linux в ядре NT. Если NTFS трудно использовать с семантикой Linux, то теперь такой проблемы нет, потому что теперь окружение Linux использует ext4 на своём виртуальном диске. И поддержка приложений Linux может быть гораздо более полной, потому что ну, потому что WSL 2 это и есть полноценный Linux.

Но подумайте, за счёт чего достигнуто это удобство? Чего мы лишились?

Какой должна была стать WSL, если бы всё работало так, как задумано:

  • Представьте, как здорово набрать ps или top в сеансе WSL и увидеть рядом процессы Linux и Windows, причём любой из них можно убить командой kill?
  • Представьте, как здорово манипулировать службами Windows из сеанса WSL?
  • Как здорово использовать ifconfig (аналог ipconfig из Windows, хотя есть ещё ip) в рамках сеанса WSL для проверки и изменения сетевых интерфейсов компьютера?


  • По сути, можете представить выполнение абсолютно всех задач системного администрирования в Windows из линуксовой консоли WSL? Это же сказка!

Хотя такого никогда не существовало, такой мир вполне можно себе представить и это могла обеспечить только архитектура WSL 1. И это вовсе не фантазии, потому что именно такую модель использует macOS (хотя это немного читерство, ведь macOS, по сути, является Unix).

Вот что бесит сильнее всего, пишет Джулио Мерино: Хотя я могу установить WSL на свою машину разработки для Azure, но никак не могу его использовать вообще ни для чего. По-прежнему приходится работать в CMD.EXE, поскольку здесь происходит взаимодействие с нативными процессами и ресурсами Windows, а инструменты, с которыми я имею дело, предназначены только для Windows.

На странице вопросов и ответов написано, что WSL 1 не будет заброшен, то есть можно запускать дистрибутивы WSL 1 и WSL 2 вместе, проапгрейдить любой дистрибутив на WSL 2 в любое время или вернуться на WSL 1. И если посмотреть на строгую приверженность к обратной совместимости Microsoft, из-за которой мы до сих пор вынуждены пользоваться архаичными инструментами и протоколами, это может быть правдой. Но поддержка WSL 1 колоссальное усилие, потому что придётся отслеживать и соответствовать всем изменениям Linux. Как бы то ни было, будем надеяться, что WSL 1 продолжит своё существование. И кто знает, вдруг когда-нибудь всё-таки воплотится в жизнь тот волшебный мир, который мы представляем?

Уровень совместимости в BSD


Интересно, что семейство операционных систем BSD ( FreeBSD, OpenBSD, NetBSD, MidnightBSD, GhostBSD, Darwin, DragonFly BSD) всегда отставали от Linux и других коммерческих операционных систем. Но у них очень давно была реализована эта совместимость на бинарном уровне, о которой мы говорим. Джулио Мерино говорит, что совместимость с Linux в ядре NetBSD была реализована ещё в 1995 году, то есть четверть века назад и за 21 год до рождения WSL 1.

И самое замечательное, эта совместимость не ограничивается только Linux. На протяжении многих лет NetBSD поддерживала эмуляцию различных операционных систем. Поддержка SVR4 появилась в 1994 году, и в течение короткого периода времени NetBSD даже поддерживала бинарные файлы PE/COFF да, правильно, это бинарные файлы Win32! Таким образом, в некотором смысле NetBSD реализовала модель WSL 1 наоборот: она позволяла запускать бинарные файлы Win32 поверх ядра NetBSD ещё в 2002 году. Вот так вот.



На правах рекламы


VDSina предлагает VDS в аренду под любые задачи, огромный выбор операционных систем для автоматической установки, есть возможность установить любую ОС с собственного ISO, удобная панель управления собственной разработки и посуточная оплата тарифа, который вы можете создать индивидуально под свои задачи.

Подробнее..

Paragon открыла свой драйвер NTFS для Linux, предложив включить его в ядро

16.08.2020 20:19:14 | Автор: admin


Компания Paragon Software опубликовала набор патчей с полноценной реализаций файловой системы NTFS, открыв код драйвера под лицензией GPL. Драйвер поддерживает работу как в режиме чтения, так и в режиме записи.

По словам разработчиков, эта реализация поддерживает все возможности версии NTFS 3.1.Сейчас в драйвере используется урезанная реализация журнала NTFS. Но в будущем Paragon Software добавит поддержку полного журналирования поверх уже встроенного в ядро универсального блочного устройства JBD, на базе которого организовано журналирование в ext3, ext4, OCFS2.

Среди поддерживаемых возможностей:
  • Расширенные атрибуты файлов.
  • Режим сжатия данных.
  • Эффективная работа с пустотами в файлах.
  • Воспроизведение изменений из журнала для восстановления целостности после сбоев.


Источник: servernews.ru

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

Проблема с нативным драйвером NTFS от Tuxera в том, что он не поддерживает большое количество возможностей, включая запись. Плюс ко всему, он давно не развивается. В качестве альтернативы есть решение NTFS-3G, которое работает через FUSE в пространстве пользователя. Оно поддерживает возможности, отсутствующие в нативном драйвере, но у него есть проблемы с производительностью на старых и слабых системах. Но и этот драйвер не обновлялся с 2017 года. Оба драйвера созданы компанией Tuxera.

У предложенного Paragon патча есть проблемы. Например, он включает 27 тысяч строк. Это слишком много, поскольку при рецензировании и проверке возникают сложности. Кроме того, сторонние разработчики предложили явно определить политику дальнейшего сопровождения кода в файле MAINTAINERS, плюс указать Git-ветку, в которую нужно присылать исправления.

В конце прошлого года компания Paragon открыла код драйвера с реализацией файловой системы exFAT. Первая версия драйвера была ограничена работой в режиме только для чтения, но затем появился и вариант с поддержкой записи. Эти патчи так и не были включены в основной состав ядра. Вместо них приняли аналог от Samsung, который применяется в прошивках Android-смартфонов.
Подробнее..

NTFS Reparse Points

23.11.2020 12:04:01 | Автор: admin
Привет, Хабр. Представляю вам гайд по NTFS Reparse points (далее RP), точкам повторной обработки. Это статья для тех, кто только начинает изучать тонкости разработки ядра Windows и его окружения. В начале объясню теорию с примерами, потом дам интересную задачку.



RP является одной из ключевых фич файловой системы NTFS, которая бывает полезна в решении задач резервного копирования и восстановления. Как следствие, Acronis очень заинтересован в данной технологии.

Полезные ссылки


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


Теория


Точка повторной обработки или Reparse Point (RP) это объект заданного размера с данными, определенными программистом, и уникальным тегом. Пользовательский объект представлен структурой REPARSE_GUID_DATA_BUFFER.

typedef struct _REPARSE_GUID_DATA_BUFFER {  ULONG  ReparseTag;  USHORT ReparseDataLength;  USHORT Reserved;  GUID   ReparseGuid;  struct {    UCHAR DataBuffer[1];  } GenericReparseBuffer;} REPARSE_GUID_DATA_BUFFER, *PREPARSE_GUID_DATA_BUFFER;

Размер блока данных RP доходит до 16 килобайт.
ReparseTag 32-х разрядный тэг.
ReparseDataLength размер данных
DataBuffer указатель на пользовательские данные

RP, предоставленные Microsoft, могут быть представлены структурой REPARSE_DATA_BUFFER. Не следует её использовать для самописных RP.

Рассмотрим формат тега:



M Бит Майкрософт; Если этот бит установлен, значит тег разработан компанией Майкрософт.
L Бит задержки; Если этот бит установлен, значит данные, на которые ссылается RP расположены на носителе с низкой скоростью реакции и большой задержкой выдачи данных.
R Зарезервированный бит;
N Бит замены имени; Если этот бит установлен, значит файл или каталог представляет другую именованную сущность в файловой системе.
Значение тега Запрашивается у Microsoft;

Каждый раз, когда приложение создает или удаляет RP, NTFS обновляет файл метаданных \\$Extend\\$Reparse. Именно в этом файле хранятся записи о RP. Такое централизованное хранение позволяет сортировать и эффективно искать необходимый объект любому приложению.

Через механизм RP в Windows реализована поддержка символьных ссылок, системы удаленного хранилища, а также точек монтирования томов и каталогов.

Жесткие ссылки в системе Windows не являются фактическим объектом, а просто синонимом на один и тот же файл на диске. Это не отдельные объекты файловой системы, а просто еще одно наименование файла в таблице расположения файлов. Этим жёсткие ссылки отличаются от символьных.

Для использования технологии RP нам нужно написать:

  • Небольшое приложение с привилегиями SE_BACKUP_NAME или SE_RESTORE_NAME, которое будет создавать файл содержащий структуру RP, устанавливать обязательное поле ReparseTag и заполнять DataBuffer
  • Драйвер режима ядра, который будет читать данные буфера и обрабатывать обращения к этому файлу.

Создадим свой файл с RP


1. Получаем необходимые привилегии

void GetPrivilege(LPCTSTR priv){HANDLE hToken;TOKEN_PRIVILEGES tp;OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES, &hToken);LookupPrivilegeValue(NULL, priv, &tp.Privileges[0].Luid);tp.PrivilegeCount = 1;tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;AdjustTokenPrivileges(hToken, FALSE, &tp,sizeof(TOKEN_PRIVILEGES), NULL, NULL);CloseHandle(hToken);}GetPrivilege(SE_BACKUP_NAME);GetPrivilege(SE_RESTORE_NAME);GetPrivilege(SE_CREATE_SYMBOLIC_LINK_NAME);

2. Подготавливаем структуру REPARSE_GUID_DATA_BUFFER. Для примера в данные RP мы напишем простую строку My reparse data.

TCHAR data[] = _T("My reparse data");BYTE reparseBuffer[sizeof(REPARSE_GUID_DATA_BUFFER) + sizeof(data)];PREPARSE_GUID_DATA_BUFFER rd = (PREPARSE_GUID_DATA_BUFFER) reparseBuffer;ZeroMemory(reparseBuffer, sizeof(REPARSE_GUID_DATA_BUFFER) + sizeof(data));// {07A869CB-F647-451F-840D-964A3AF8C0B6}static const GUID my_guid = { 0x7a869cb, 0xf647, 0x451f, { 0x84, 0xd, 0x96, 0x4a, 0x3a, 0xf8, 0xc0, 0xb6 }};rd->ReparseTag = 0xFF00;rd->ReparseDataLength = sizeof(data);rd->Reserved = 0;rd->ReparseGuid = my_guid;memcpy(rd->GenericReparseBuffer.DataBuffer, &data, sizeof(data));

3. Создаем файл.

LPCTSTR name = _T("TestReparseFile");_tprintf(_T("Creating empty file\n"));HANDLE hFile = CreateFile(name,GENERIC_READ | GENERIC_WRITE, 0, NULL,CREATE_NEW, FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS,NULL);if (INVALID_HANDLE_VALUE == hFile){_tprintf(_T("Failed to create file\n"));return -1;}

4. Заполняем файл нашей структурой, используя метод DeviceIoControl с параметром FSCTL_SET_REPARSE_POINT.

_tprintf(_T("Creating reparse\n"));if (!DeviceIoControl(hFile, FSCTL_SET_REPARSE_POINT, rd, rd->ReparseDataLength + REPARSE_GUID_DATA_BUFFER_HEADER_SIZE, NULL, 0, &dwLen, NULL)){CloseHandle(hFile);DeleteFile(name);_tprintf(_T("Failed to create reparse\n"));return -1;}CloseHandle(hFile);

Полный код этого приложения можно найти тут.

После сборки и запуска у нас образуется файл. Утилита fsutil поможет посмотреть на созданный нами файл и убедиться, что наши данные на месте.



Обработка RP


Пришло время взглянуть на этот файл со стороны ядреного пространства. Я не буду углубляться в детали устройства мини-фильтра драйвера. Хорошее объяснение есть официальной документации от Microsoft с примерами кода. А мы посмотрим на post callback метод.

Нужно перезапросить IRP c параметром FILE_OPEN_REPARSE_POINT. Для этого мы вызовем FltReissueSynchronousIo. Данная функция повторит запрос, но уже с обновленными Create.Options.

Внутри структуры PFLT_CALLBACK_DATA есть поле TagData. Если вызвать метод FltFsControlFile с кодом FSCTL_GET_REPARSE_POINT, то получим наш буфер с данными.

// todo конечно стоит проверить наш ли это тэг, а не только его наличиеif (Data->TagData != NULL) {if ((Data->Iopb->Parameters.Create.Options & FILE_OPEN_REPARSE_POINT) != FILE_OPEN_REPARSE_POINT)    {      Data->Iopb->Parameters.Create.Options |= FILE_OPEN_REPARSE_POINT;      FltSetCallbackDataDirty(Data);      FltReissueSynchronousIo(FltObjects->Instance, Data);    }    status = FltFsControlFile(      FltObjects->Instance,       FltObjects->FileObject,       FSCTL_GET_REPARSE_POINT,       NULL,       0,       reparseData,      reparseDataLength,      NULL    );}



Далее можно использовать эти данные в зависимости от задачи. Можно вновь перезапросить IRP. Или инициировать совершенно новый запрос. К примеру, в проекте LazyCopy в данных RP хранится путь до оригинального файла. Автор не начинает копирование в момент открытия файла, а лишь пересохраняет данные из RP в stream context данного файла. Данные начинают копироваться в момент чтения или записи файла. Вот наиболее важные моменты его проекта:

// Operations.c - PostCreateOperationCallbackNT_IF_FAIL_LEAVE(LcGetReparsePointData(FltObjects, &fileSize, &remotePath, &useCustomHandler));NT_IF_FAIL_LEAVE(LcFindOrCreateStreamContext(Data, TRUE, &fileSize, &remotePath, useCustomHandler, &streamContext, &contextCreated));// Operations.c - PreReadWriteOperationCallbackstatus = LcGetStreamContext(Data, &context);NT_IF_FAIL_LEAVE(LcGetFileLock(&nameInfo->Name, &fileLockEvent));NT_IF_FAIL_LEAVE(LcFetchRemoteFile(FltObjects, &context->RemoteFilePath, &nameInfo->Name, context->UseCustomHandler, &bytesFetched));NT_IF_FAIL_LEAVE(LcUntagFile(FltObjects, &nameInfo->Name));NT_IF_FAIL_LEAVE(FltDeleteStreamContext(FltObjects->Instance, FltObjects->FileObject, NULL));

На самом деле, RP имеют широкий спектр применения, и открывают множество возможностей для решения различных задач. Один из них разберем на решении следующей задачи.

Задачка


Игрушка Half-life умеет запускаться в двух режимах: software mode и hardware mode, которые отличаются способом отрисовки графики в игре. Немного поковыряв игрушку в IDA Pro, можно заметить, что режимы различаются загружаемой методом LoadLibrary библиотекой: sw.dll или hw.dll.



В зависимости от входных аргументов (например -soft) выбирается та или иная строка и подбрасывается в вызов функции LoadLibrary.



Суть задачи заключается в том, чтобы запретить игрушке загружаться в software mode, да так, чтобы пользователь этого не заметил. В идеале чтобы пользователь даже не понял, что вне зависимости от его выбора, он загружается в hardware mode.

Конечно, можно было бы пропатчить исполняемый файл, или заменить dll файл, да даже просто скопировать hw.dll и переименовать копию в sw.dll, но мы не ищем лёгких путей. Плюс, если игру будут обновлять или переустанавливать, эффект пропадет.

Решение


Я предлагаю следующее решение: напишем небольшой мини-фильтр драйвер. Он будет работать постоянно и на него не будут влиять переустановка и обновление игры. Зарегистрируем драйвер на операцию IRP_MJ_CREATE, ведь каждый раз, когда исполняемый файл вызывает LoadLibrary, он по сути открывает файл библиотеки. Как только мы заметим, что процесс игры пытается открыть библиотеку sw.dll, мы вернем статус STATUS_REPARSE и попросим повторить запрос, но уже на открытие hw.dll. Результат: открылась нужная нам библиотека, хоть user space и просил у нас другую.

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

NT_IF_FAIL_LEAVE(PsSetCreateProcessNotifyRoutine(IMCreateProcessNotifyRoutine, FALSE));

В этом методе мы должны получить имя запускаемого файла. Для этого можно воспользоваться ZwQueryInformationProcess.

NT_IF_FAIL_LEAVE(PsLookupProcessByProcessId(ProcessId, &eProcess));NT_IF_FAIL_LEAVE(ObOpenObjectByPointer(eProcess, OBJ_KERNEL_HANDLE, NULL, 0, 0, KernelMode, &hProcess));NT_IF_FAIL_LEAVE(ZwQueryInformationProcess(hProcess,                                               ProcessImageFileName,                                               buffer,                                               returnedLength,                                               &returnedLength));

Если он совпадает с искомым, в нашем случае hl.exe, то необходимо запомнить его PID.

target = &Globals.TargetProcessInfo[i];if (RtlCompareUnicodeString(&processNameInfo->Name, &target->TargetName, TRUE) == 0){      target->NameInfo = processNameInfo;      target->isActive = TRUE;      target->ProcessId = ProcessId;      LOG(("[IM] Found process creation: %wZ\n", &processNameInfo->Name));}

Итак, теперь у нас в глобальном объекте сохранен PID процесса нашей игры. Можно переходить к pre create callback. Там мы должны получить имя открываемого файла. В этом нам поможет FltGetFileNameInformation. Данную функцию нельзя вызывать на DPC уровне прерываний (читай подробнее про IRQL), однако, мы собираемся делать вызов исключительно на pre create, что гарантирует нам уровень не выше APC.

status = FltGetFileNameInformation(Data, FLT_FILE_NAME_OPENED | FLT_FILE_NAME_QUERY_FILESYSTEM_ONLY | FLT_FILE_NAME_ALLOW_QUERY_ON_REPARSE, &fileNameInfo);

Далее все просто, если наше имя sw.dll, то нужно заменить его в FileObject на hw.dll. И вернуть статус STATUS_REPARSE.

// may be it is swif (RtlCompareUnicodeString(&FileNameInfo->Name, &strSw, TRUE) == 0){// concatNT_IF_FAIL_LEAVE(IMConcatStrings(&replacement, &FileNameInfo->ParentDir, &strHw));// then need to changeNT_IF_FAIL_LEAVE(IoReplaceFileObjectName(FileObject, replacement.Buffer, replacement.Length));}Data->IoStatus.Status = STATUS_REPARSE;Data->IoStatus.Information = IO_REPARSE;return FLT_PREOP_COMPLETE;

Безусловно, реализация проекта целиком несколько более комплексная, но основные моменты я постарался раскрыть. Весь проект с подробностями тут.

Тестируем


Чтобы упростить наши тестовые запуски, вместо игры будем запускать небольшое приложение и библиотеки следующего содержания:

// testapp.exe#include "TestHeader.h"int main(){TestFunction();return 0;}// testdll0.dll#include "../include/TestHeader.h"#include <iostream>// This is an example of an exported function.int TestFunction(){std::cout << "hello from test dll 0" << std::endl;return 0;}// testdll1.dll#include "../include/TestHeader.h"#include <iostream>// This is an example of an exported function.int TestFunction(){std::cout << "hello from test dll 1" << std::endl;return 0;}

Соберем testapp.exe с testdll0.dll, и копируем их на виртуалку (а именно там мы планируем запускать), а также подготовим testdll1.dll. Задачей нашего драйвера будет заменить testdll0 на testdll1. Мы поймем что у нас все получилось, если в консоли мы увидим сообщение hello from test dll 1, вместо hello from test dll 0. Запустим без драйвера, чтобы убедиться, что наше тестовое приложение работает корректно:



А теперь установим и запустим драйвер:



Теперь, запуская тоже самое приложение, мы получим совершенно другой вывод в консоль, так как загрузилась другая библиотека



Плюс в приложении, написанном для драйвера, мы видим логи, которые говорят, что мы действительно поймали наш запрос на открытие и заменили один файл на другой. Тест удался, пришло время проверить решение на самой игре. Наслаждаемся :)



Надеюсь, было полезно и интересно. Пишите ваши вопросы в комментариях, постараюсь ответить.
Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru