Хорошо ли использовать ntdll.dll в консольном приложении win32? - PullRequest
0 голосов
/ 09 мая 2019

Коротко: в моем проекте c ++ мне нужно читать / писать расширенные свойства файла.Мне это удалось с помощью альтернативных потоков данных (ADS).Моя проблема заключается в том, что для открытия ADS мне нужно использовать CreateFile API.Но это не удовлетворяет мои потребности.NtCreateFile удовлетворит все мои потребности.(Или же NtSetEaFile и NtQueryEaFile) Но NtCreateFile напрямую недоступен из консольного приложения win32.

Я знаю, что могу легко использовать эту функцию через GetProcAdres s.Но мне нравится знать мнение всех вас, если я что-то пропустил?Некоторые другие библиотеки уже используют этот шаблон, например, Chromium (https://github.com/chromium-googlesource-mirror/chromium/blob/1c1996b75d3611f56d14e2b30e7ae4eabc101486/src/sandbox/src/win_utils.cc function: ResolveNTFunctionPtr), но я не уверен, потому что проект c ++ - это не хобби, и я спрашиваю себя, опасно это или нет.

Я думаю, NtCreateFile, возможно, самый безопасный способ сделать это, потому что он хорошо документирован и поддерживается заголовком winternl.h.Тем более, что этот метод остается неизменным со времен Windows 2000. Но что с NtSetEaFile, NtQueryEaFile, которые идеально соответствуют моим потребностям.Они только наполовину документированы.Документация для ZwSetEaFile и ZwQueryEaFile существует (без изменений, начиная с Windows 2000).

Причина, по которой я хочу это сделать:

Я хочу писать и читать расширенные свойства из файлов через ADS,Но в случае написания расширенного свойства данного файла в первый раз, мне нужно открыть файл с OPEN_ALWAYS.Если файл не существует, он создаст новый файл, даже если я получу доступ только к потоку содержимого файла.Чтобы избежать этого, я сначала получаю дескриптор исходного файла и проверяю с помощью этой РУЧКИ, существует ли файл.Но я не хочу писать в блоге какие-либо файлы с ограниченными правами доступа, потому что, с моей точки зрения, это очень плохая модель.Пользователь должен иметь полный доступ к любому файлу в любое время.Из-за этого мы открываем все РУЧКИ с флагом FILE_SHARE_DELETE |FILE_SHARE_READ |FILE_SHARE_WRITE.И теперь у меня есть гонка.

auto hFile = CreateFileW(originalPath, …, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, …).
// this is the little race: if somebody at least rename originalPath the
// second CreateFileW call will cause the creation of a empty file with the
// path originalPath (the old path).
auto hADS = CreateFileW(originalPath + adsName, …, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, OPEN_ALWAYS, …).

Это главная проблема, особенно потому, что это время от времени происходит в наших тестах.NtCreateFile исправит это, потому что я могу создать второй HANDLE с помощью первого HANDLE.Из-за этого нет расы.Или NtSetEaFile и NtQueryEaFile помогут, потому что мне нужна только одна РУЧКА.

Дело в том, что приложение не нужно сохранять в будущем, потому что ADS работает только на NTFS.И кто знает, когда NTFS будет обменен.Но я не хочу грубого поведения.Я хочу доверять этим методам.Я в порядке, если API изменится в будущем, и программное обеспечение должно адаптироваться к нему.Но я хочу быть уверен, что все Windows выше или равные 7 могут справиться с этим.Кто-нибудь поделиться опытом?Я бы очень хотел их услышать.

Ответы [ 2 ]

3 голосов
/ 09 мая 2019

Этот вопрос неправильный. Предлагаемое решение для вашей проблемы не использует NtCreateFile, но использует CreateFile с dwCreationDisposition, установленным в OPEN_EXISTING.

Из документации :

OPEN_EXISTING

Открывает файл или устройство, только если оно существует. Если указанный файл или устройство не существует, функция не работает и код последней ошибки установлено в ERROR_FILE_NOT_FOUND.

Просто откройте файл, если он существует, и установите все, что вы хотите. Если файл переименован, CreateFile возвращает ERROR_FILE_NOT_FOUND.

ПРОБЛЕМА

Теперь, к вашему предложенному решению, какой метод лучше или почему невозможно использовать ntdll.dll в консольном приложении win32 (???). Опять же, ваш «лучший» метод - GetProcAddress «неправильный» так же, как и использование ссылок на ntdll.dll. В Windows 11 или Windows 12 или Windows 3030 эта функция может быть удалена, и оба решения (статический или динамический импорт) завершатся с ошибкой.

0 голосов
/ 16 мая 2019

Нередко использование такого рода API-интерфейсов небезопасно, если они являются документацией В случае NtSetEaFile, NtQueryEaFile и NtCreateFile вы можете найти описание в документе Microsoft. (имейте в виду, NtXxx == ZwXxx)

Но этот API может измениться в будущем, и Microsoft не гарантирует, что он предоставит те же методы в следующей версии Windows. Если вы можете, используйте общедоступный API, потому что тогда вы в безопасности. Если нет, то решение принимается индивидуально. В этом случае три метода API остаются неизменными, начиная с Windows2000. Плюс, например, NtSetEaFile и NtQueryEaFile используется Microsoft для WSL (подсистема Windows для Linux). И особенно NtCreateFile используется широким спектром проектов OpenSource. Поэтому маловероятно, что этот API изменится.

В моем случае использования важен другой аспект. Потому что я хотел использовать ADS, но ADS поддерживается только NTFS. Таким образом, использование ADS не гарантирует совместимость в будущем. Так что для меня было очень ясно использовать NtSetEaFile и NtQueryEaFile.

Но как вы можете использовать этот вид API? Возможно динамическое или статическое связывание. Это зависит от ваших потребностей, что лучше. В случае статического связывания вам нужно скачать последний WDK (Windows Driver Kit) и связать его с ntdll.lib. В случае динамического связывания вы можете получить доступ к dll напрямую через GetModuleHandle и узнать адрес метода с помощью GetProcAddress. Под Windows ntdll.dll доступен из любого приложения. В обоих случаях у вас нет непосредственно заголовочного файла. Вы должны определить заголовочный файл самостоятельно или использовать WDK, чтобы получить их.

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

Простой псевдокод (динамический регистр):

typedef struct _FILE_FULL_EA_INFORMATION {
    ULONG  NextEntryOffset;
    UCHAR  Flags;
    UCHAR  EaNameLength;
    USHORT EaValueLength;
    CHAR   EaName[1];
} FILE_FULL_EA_INFORMATION, *PFILE_FULL_EA_INFORMATION;

typedef struct _IO_STATUS_BLOCK {
    union {
        NTSTATUS Status;
        PVOID    Pointer;
    };
    ULONG_PTR Information;
} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;

typedef NTSTATUS(WINAPI *NtSetEaFileFunction)(IN HANDLE FileHandle,
                                              OUT       PIO_STATUS_BLOCK
                                                        IoStatusBlock,
                                              IN PVOID Buffer,
                                              IN ULONG Length);

HMODULE  ntdll                   = GetModuleHandle(L"ntdll.dll");
NtSetEaFileFunction function     = nullptr;
FARPROC *function_ptr            = reinterpret_cast<FARPROC *>(&function);

*function_ptr = GetProcAddress(ntdll, "NtQueryEaFile");

// function could be used normally.

Другой ответ неверен. Причина в том, что причина моей проблемы в том, что мне нужно использовать OPEN_ALWAYS. Конечно, если вам не нужен этот флаг, все в порядке. Но в моем случае есть момент, когда мне нужно было создать ADS. И он не будет создан без флага OPEN_ALWAYS.

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