在 win32 控制台应用程序中使用 ntdll.dll 好吗?
Is it good to use ntdll.dll in a win32 console application?
简短:
在我的 C++ 项目中,我需要 read/write 扩展文件属性。我使用备用数据流 (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 函数:ResolveNTFunctionPtr
)
但我不确定,因为c++项目不是一个业余项目,我问自己是否危险。
我想 NtCreateFile
可能是最安全的方法,因为 winternl.h
header 有详细的文档和支持。特别是因为此方法自 windows 2000 年以来未发生变化。但是 NtSetEaFile
、NtQueryEaFile
的内容完全符合我的需求。他们只记录了一半。 ZwSetEaFile
和 ZwQueryEaFile
的文档存在(自 windows 2000 以来未更改)。
我想这样做的原因:
我想通过 ADS 写入和读取文件的扩展属性。但是如果第一次写入给定文件的扩展 属性,我需要用 OPEN_ALWAYS
打开文件。如果文件不存在,它将创建一个新文件,即使我只访问而不是文件的内容流。为避免这种情况,我首先获取原始文件的句柄并检查此句柄是否文件仍然存在。
但我不想在博客上发布任何访问权限降低的文件,因为从我的角度来看,这是一种非常糟糕的模式。用户需要随时拥有对任何文件的完全访问权限。因此,我们打开所有带有标志 FILE_SHARE_DELETE
| 的 HANDLES。 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
会有所帮助,因为我只需要一个 HANDLE。
问题是,应用程序不需要为将来保存,因为 ADS 无论如何只能在 NTFS
上运行。谁知道什么时候会交换NTFS
。但我不想要一个不稳定的行为。我想相信这个方法。如果API以后会发生变化,软件需要适应它,我很好。但我想确定,所有高于或等于 7 的 Windows 都可以处理它。有人可以分享一些经验吗?我很想听听他们的声音。
这道题错了。您针对问题提出的解决方案不是使用 NtCreateFile,而是使用 CreateFile 并将 dwCreationDisposition 设置为 OPEN_EXISTING.
OPEN_EXISTING
Opens a file or device, only if it exists. If the specified file or
device does not exist, the function fails and the last-error code is
set to ERROR_FILE_NOT_FOUND.
只需打开文件(如果存在)并设置您想要的任何内容。如果文件被重命名,CreateFile returns ERROR_FILE_NOT_FOUND.
问题
现在,对于您提出的解决方案,什么是更好的方法或者为什么不能在 win32 控制台应用程序中使用 ntdll.dll (???)。
同样,您的“更好”方法 - GetProcAddress 与使用针对 ntdll.dll 的链接一样“错误”。在Windows11,或Windows12或Windows3030 该功能可能会被删除,两种解决方案(静态导入与动态导入)都将失败。
如果是文档,使用这种 API 并不是真的不安全。在 NtSetEaFile
、NtQueryEaFile
和 NtCreateFile
的情况下,您可以在 Microsoft 的文档中找到描述。 (记住 NtXxx == ZwXxx)
但是这个API将来可能会改变,微软不保证在下一个Windows版本中会提供相同的方法。如果可以,请使用 public API,因为那样您就安全了。如果不是,则视具体情况而定。在这种情况下,API 中的三种方法自 Windows2000 以来未发生变化。另外,例如 NtSetEaFile
和 NtQueryEaFile
被 Microsoft 用于 WSL(Windows 子系统用于 Linux)。特别是 NtCreateFile
被广泛的开源项目使用。所以这个 API 改变的可能性很小。
在我的用例中,另一个方面很重要。因为我想用ADS,但是ADS只有NTFS支持。所以使用 ADS 也不能保证未来的兼容性。所以我很清楚使用 NtSetEaFile
和 NtQueryEaFile
.
但是如何使用这种 API?动态或静态 linking 是可能的。这取决于您的需求,哪个更好。如果是静态 linking,您需要针对 ntdll.lib 下载最后一个 WDK(Windows 驱动程序包)和 link。在动态 linking 的情况下,您可以通过 GetModuleHandle
直接访问 dll 并使用 GetProcAddress
找出方法的地址。在 Windows ntdll.dll
下可以从任何应用程序访问。在这两种情况下,您都没有直接的头文件。需要自己定义头文件或者使用WDK获取。
在我的项目中,动态 linking 是最佳选择。原因是,在每个 windows 上都会选择正确的实现,如果该方法不可用,我有机会停用我的软件中的功能而不是崩溃。由于最后一个原因,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
。当然,如果你不需要这个flag,就万事大吉了。但就我而言,有时我需要创建 ADS。如果没有 OPEN_ALWAYS
标志,它将不会被创建。
简短:
在我的 C++ 项目中,我需要 read/write 扩展文件属性。我使用备用数据流 (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 函数:ResolveNTFunctionPtr
)
但我不确定,因为c++项目不是一个业余项目,我问自己是否危险。
我想 NtCreateFile
可能是最安全的方法,因为 winternl.h
header 有详细的文档和支持。特别是因为此方法自 windows 2000 年以来未发生变化。但是 NtSetEaFile
、NtQueryEaFile
的内容完全符合我的需求。他们只记录了一半。 ZwSetEaFile
和 ZwQueryEaFile
的文档存在(自 windows 2000 以来未更改)。
我想这样做的原因:
我想通过 ADS 写入和读取文件的扩展属性。但是如果第一次写入给定文件的扩展 属性,我需要用 OPEN_ALWAYS
打开文件。如果文件不存在,它将创建一个新文件,即使我只访问而不是文件的内容流。为避免这种情况,我首先获取原始文件的句柄并检查此句柄是否文件仍然存在。
但我不想在博客上发布任何访问权限降低的文件,因为从我的角度来看,这是一种非常糟糕的模式。用户需要随时拥有对任何文件的完全访问权限。因此,我们打开所有带有标志 FILE_SHARE_DELETE
| 的 HANDLES。 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
会有所帮助,因为我只需要一个 HANDLE。
问题是,应用程序不需要为将来保存,因为 ADS 无论如何只能在 NTFS
上运行。谁知道什么时候会交换NTFS
。但我不想要一个不稳定的行为。我想相信这个方法。如果API以后会发生变化,软件需要适应它,我很好。但我想确定,所有高于或等于 7 的 Windows 都可以处理它。有人可以分享一些经验吗?我很想听听他们的声音。
这道题错了。您针对问题提出的解决方案不是使用 NtCreateFile,而是使用 CreateFile 并将 dwCreationDisposition 设置为 OPEN_EXISTING.
OPEN_EXISTING
Opens a file or device, only if it exists. If the specified file or device does not exist, the function fails and the last-error code is set to ERROR_FILE_NOT_FOUND.
只需打开文件(如果存在)并设置您想要的任何内容。如果文件被重命名,CreateFile returns ERROR_FILE_NOT_FOUND.
问题
现在,对于您提出的解决方案,什么是更好的方法或者为什么不能在 win32 控制台应用程序中使用 ntdll.dll (???)。 同样,您的“更好”方法 - GetProcAddress 与使用针对 ntdll.dll 的链接一样“错误”。在Windows11,或Windows12或Windows3030 该功能可能会被删除,两种解决方案(静态导入与动态导入)都将失败。
如果是文档,使用这种 API 并不是真的不安全。在 NtSetEaFile
、NtQueryEaFile
和 NtCreateFile
的情况下,您可以在 Microsoft 的文档中找到描述。 (记住 NtXxx == ZwXxx)
但是这个API将来可能会改变,微软不保证在下一个Windows版本中会提供相同的方法。如果可以,请使用 public API,因为那样您就安全了。如果不是,则视具体情况而定。在这种情况下,API 中的三种方法自 Windows2000 以来未发生变化。另外,例如 NtSetEaFile
和 NtQueryEaFile
被 Microsoft 用于 WSL(Windows 子系统用于 Linux)。特别是 NtCreateFile
被广泛的开源项目使用。所以这个 API 改变的可能性很小。
在我的用例中,另一个方面很重要。因为我想用ADS,但是ADS只有NTFS支持。所以使用 ADS 也不能保证未来的兼容性。所以我很清楚使用 NtSetEaFile
和 NtQueryEaFile
.
但是如何使用这种 API?动态或静态 linking 是可能的。这取决于您的需求,哪个更好。如果是静态 linking,您需要针对 ntdll.lib 下载最后一个 WDK(Windows 驱动程序包)和 link。在动态 linking 的情况下,您可以通过 GetModuleHandle
直接访问 dll 并使用 GetProcAddress
找出方法的地址。在 Windows ntdll.dll
下可以从任何应用程序访问。在这两种情况下,您都没有直接的头文件。需要自己定义头文件或者使用WDK获取。
在我的项目中,动态 linking 是最佳选择。原因是,在每个 windows 上都会选择正确的实现,如果该方法不可用,我有机会停用我的软件中的功能而不是崩溃。由于最后一个原因,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
。当然,如果你不需要这个flag,就万事大吉了。但就我而言,有时我需要创建 ADS。如果没有 OPEN_ALWAYS
标志,它将不会被创建。