【问题标题】:Is it good to use ntdll.dll in a win32 console application?在win32控制台应用程序中使用ntdll.dll好吗?
【发布时间】:2019-05-09 09:41:22
【问题描述】:

短: 在我的 c++ 项目中,我需要读/写扩展文件属性。我使用备用数据流 (ADS) 来管理它。我的问题是,为了打开 ADS,我需要使用 CreateFile API。但这并不能满足我的需求。 NtCreateFile 将满足我的所有需求。 (或者NtSetEaFileNtQueryEaFile)但是NtCreateFile 不能直接从win32 控制台应用程序访问。

我知道我可以通过GetProcAdress 轻松使用此功能。但我想知道你们所有人的意见,如果我错过了什么?其他一些库已经在使用这种模式,例如 Chromium (https://github.com/chromium-googlesource-mirror/chromium/blob/1c1996b75d3611f56d14e2b30e7ae4eabc101486/src/sandbox/src/win_utils.cc function: ResolveNTFunctionPtr) 但我不确定,因为 c++ 项目不是一个爱好项目,我问自己是否危险。

我猜NtCreateFile 可能是最安全的方法,因为它有很好的文档记录并得到winternl.h 标头的支持。特别是因为这种方法自 Windows 2000 以来没有改变。但是 NtSetEaFileNtQueryEaFile 完全符合我的需求。他们只记录了一半。存在 ZwSetEaFileZwQueryEaFile 的文档(自 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。因为没有种族。或者 NtSetEaFileNtQueryEaFile 会有所帮助,因为我只需要一个 HANDLE。

问题是,应用程序不需要保存以备将来使用,因为无论如何 ADS 仅适用于 NTFS。谁知道NTFS什么时候会被交换。但我不想要一个片状的行为。我想相信这个方法。如果 API 将来会发生变化并且软件需要适应它,我很好。但我想确定的是,所有高于或等于 7 的 Windows 都可以处理它。有人分享一些经验吗?我很想听听他们的声音。

【问题讨论】:

  • 但 NtCreateFile 不能从 win32 控制台应用程序直接访问这是不正确的。它的访问方式与CreateFile 相同。只需与 ntdll[p].lib 链接即可。控制台或非控制台不起任何作用。和 ADS != EA,你总是把它弄糊涂了。你想用EA我怎么理解
  • 存在 ZwSetEaFile 和 ZwQueryEaFile 的文档 是的。在用户模式下 NtXxx == ZwXxx 两个名字都指向同一个函数
  • @RbMm 感谢您的遮阳篷。我对 ADS 和 EA 并不感到困惑。但是这两个概念都满足了我的需求。因为使用 ADS 我试图复制 EA 的行为。你确定链接到 ntdll[p].lib。我甚至可以使用该库访问 NtSetEaFile 吗?
  • 我还没弄清楚如何链接到库。因为我认为 ntdll.dll 的概念是,它可以随着版本的不同而变化,包括 api。因此,我无法将静态链接到它。你知道一些示例项目吗?我找不到它们中的任何一个,这对我来说看起来很可靠。因此,如果我针对最新的 WDK 进行编译,则 lib 不仅仅包含对 dll 的地址的引用,那么我在另一边读到了什么?并且因为 WDK 保证可以与 WINdows 7-10 一起使用,所以我省了?
  • 我无法将静态链接到它。 - 这是错误的。你可以链接它。绝对与您链接 kernel32.libuser32.lib 等的方式相同。

标签: c++ winapi visual-c++ file-io


【解决方案1】:

这个问题是错误的。您为您的问题提出的解决方案不是使用 NtCreateFile,而是使用 CreateFile 并将 dwCreationDisposition 设置为 OPEN_EXISTING。

来自documentation

OPEN_EXISTING

仅当文件或设备存在时才打开它。如果指定的文件或 设备不存在,函数失败,最后一个错误代码是 设置为 ERROR_FILE_NOT_FOUND。

只需打开文件(如果存在)并设置您想要的任何内容。如果文件被重命名,CreateFile 返回 ERROR_FILE_NOT_FOUND。

问题

现在,对于您提出的解决方案,有什么更好的方法或为什么不能在 win32 控制台应用程序中使用 ntdll.dll (???)。 同样,您的“更好”方法 - GetProcAddress 与使用针对 ntdll.dll 的链接是“错误的”。在 Windows 11Windows 12Windows 3030 中,该功能可能会被删除,两种解决方案(静态与动态导入)都将失败。

【讨论】:

  • 我从调查中了解到,在win32控制台应用程序中使用ntdll.dll并没有错。但是如果你使用它们,API 的未来方法可能会消失或改变。现在它们是你拥有的两个选项。将静态链接到 ntdll.lib。在这种情况下,它至少适用于所用 WDK 支持的每个版本。但是将来如果使用的 API 发生变化,您的软件可能会崩溃。或者你动态链接。在这种情况下,软件有机会对此问题作出反应并通知用户。因此,微软建议第二种方式。所以我不同意你的表述“错误”。
  • 您的遮阳篷错误。因为如果我不使用 OPEN_ALWAYS 创建备用数据流,我该如何创建它。所以请,为什么这有 3 个赞成票而我得到 3 个反对票。真的,为什么?这个遮阳篷没有解决我的问题。首先我认为他是对的,但我编译了错误的代码。但是如果我将 OPEN_ALWAYS 更改为 OPEN_ESISTING,我将无法创建 ADS。 (我当然知道)所以请。 ...
【解决方案2】:

如果它们是文档,那么使用这种 API 并不是不安全的。对于NtSetEaFileNtQueryEaFileNtCreateFile,您可以在 Microsoft 的文档中找到说明。 (记住 NtXxx == ZwXxx)

但此 API 将来可能会发生变化,Microsoft 不保证它会在下一个 Windows 版本中提供相同的方法。如果可以,请使用公共 API,因为那样你是安全的。如果不是,则根据具体情况决定。在这种情况下,API 中的三个方法自 Windows2000 以来没有改变。另外,例如 NtSetEaFileNtQueryEaFile 被 Microsoft 用于 WSL(Linux 的 Windows 子系统)。特别是NtCreateFile 被广泛的开源项目使用。所以这个 API 不太可能改变。

在我的用例中,另一个方面很重要。因为我想用ADS,但是ADS只支持NTFS。因此,使用 ADS 并不能确保未来的兼容性。所以我很清楚使用NtSetEaFileNtQueryEaFile

但是如何使用这种 API?动态或静态链接是可能的。这取决于你的需要什么更好。如果是静态链接,您需要下载最后一个 WDK(Windows 驱动程序工具包)并链接到 ntdll.lib。在动态链接的情况下,您可以通过GetModuleHandle 直接访问dll,并通过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 标志,它将不会被创建。

【讨论】:

猜你喜欢
  • 1970-01-01
  • 2011-09-30
  • 2010-11-04
  • 2011-03-10
  • 1970-01-01
  • 2017-09-15
  • 1970-01-01
  • 2015-12-11
  • 1970-01-01
相关资源
最近更新 更多