如果你在这里阅读这篇文章,这意味着你正在寻找一种方法来为你的软件转换你的 DOS 和/或 NT 路径。请放心,这就是您今天将在这里学习的内容!
问题与 Windows 路径 很清楚;这太令人困惑了。让我重复一遍,太混乱了。当您正在开发令人惊叹的软件并且需要转换 DOS 和 NT 路径时,这会成为一个问题。
在本文中,我将尽量让事情简短而有趣,所以让我们开始吧。
ntdll 中有一些未记录的函数可用于不同的转换。这些函数的列表以 Rtl* 开头,如下所示:
- RtlDosPathNameToNtPathName_U (所有窗户)
- RtlDosPathNameToNtPathName_U_WithStatus (所有窗户)
- RtlDosLongPathNameToNtPathName_U_WithStatus (窗户 10 红石 3)
- RtlDosPathNameToRelativeNtPathName_U (所有窗户)
- RtlDosPathNameToRelativeNtPathName_U_WithStatus (所有窗户)
- RtlDosLongPathNameToRelativeNtPathName_U_WithStatus (窗户 10 红石 3)
- RtlNtPathNameToDosPathName (所有窗户, 这个特定的功能非常神秘,因为您无法在任何地方找到有关它的定义或信息。但是,我们将相应地测试所有这些函数并查看它们的输出。)
上述函数具有相同的输入/输出参数:
- DosFileName (在 - 必需): 包含目标文件或目录的 DOS 名称的常量字符串。
- NtFileName ( 出——必填): 返回文件或目录的 NT 路径。必须使用不需要初始化的 UNICODE_STRING。
- FilePart (出 - 可选): 在输出时,生成路径中的文件名地址将被输入到指针中。如果此参数在输出中用 NULL 填充,则仅指定目录名称。
- RelativeName (出 - 可选): 指向包含当前目录信息的 RTL_RELATIVE_NAME_U 结构的指针。
为了我们使用这些功能,我定义了 3 条路径:
1static const wchar_t NormalNamespace[MAX_PATH] = L"C:\\Windows\\System32\\csrss.exe"; // 普通 Win32 命名空间
2static const wchar_t DeviceNamespace[MAX_PATH] = L"\\\\.\\C:\\Windows\\System32\\csrss.exe"; // Win32 设备命名空间
3static const wchar_t FileNamespace[MAX_PATH] = L"\\\\?\\C:\\Windows\\System32\\csrss.exe"; // Win32 设备命名空间
4static const wchar_t DevicePath[MAX_PATH] = L"\\Device\\HarddiskVolume1\\Windows\\System32\\csrss.exe"; // Win32 设备路径
5static const wchar_t ExtraTest[MAX_PATH] = L"directory.name\\file.name"; // 相对路径
使用 RtlDosPathNameToNtPathName\_U
此函数将返回一个 BOOLEAN。
- TRUE – 路径变换操作成功完成。
- FALSE – 路径转换操作失败。
代码:
1{
2 UNICODE_STRING Converted{};
3 RTL_RELATIVE_NAME_U RelativeName{};
4 PWSTR FilePart{};
5
6 static const wchar_t NormalNamespace[MAX_PATH] = L"C:\\Windows\\System32\\csrss.exe"; // 普通 Win32 命名空间
7 static const wchar_t DeviceNamespace[MAX_PATH] = L"\\\\.\\C:\\Windows\\System32\\csrss.exe"; // Win32 设备命名空间
8 static const wchar_t FileNamespace[MAX_PATH] = L"\\\\?\\C:\\Windows\\System32\\csrss.exe"; // Win32 设备命名空间
9 static const wchar_t DevicePath[MAX_PATH] = L"\\Device\\HarddiskVolume1\\Windows\\System32\\csrss.exe"; // Win32 设备路径
10 static const wchar_t ExtraTest[MAX_PATH] = L"directory.name\\file.name"; // 相对路径
11
12 pRtlDosPathNameToNtPathName_U _RtlDosPathNameToNtPathName_U = reinterpret_cast<pRtlDosPathNameToNtPathName_U>(GetProcAddress(GetModuleHandle(L"ntdll.dll"), "RtlDosPathNameToNtPathName_U"));
13
14 if (_RtlDosPathNameToNtPathName_U(NormalNamespace, &Converted, &FilePart, &RelativeName))
15 {
16 std::wcout << L"[NormalNamespace] (previous): " << NormalNamespace << std::endl;
17 std::wcout << L"[NormalNamespace] (converted): " << Converted.Buffer << std::endl;
18 std::wcout << L"[NormalNamespace] FilePart: " << FilePart << std::endl;
19 std::wcout << L"[NormalNamespace] RelativeName.Buffer: " << ((RelativeName.RelativeName.Buffer != NULL) ? RelativeName.RelativeName.Buffer : L"NULL") << std::endl;
20 std::wcout << L"[NormalNamespace] ContainingDirectory: " << (void*)RelativeName.ContainingDirectory << std::endl;
21 std::wcout << L"[NormalNamespace] CurDirRef->DirectoryHandle: " << ((RelativeName.CurDirRef != nullptr) ? (void*)RelativeName.CurDirRef->DirectoryHandle : L"NULL") << std::endl;
22 std::wcout << L"[NormalNamespace] CurDirRef->ReferenceCount: " << ((RelativeName.CurDirRef != nullptr) ? RelativeName.CurDirRef->ReferenceCount : 0UL) << std::endl;
23 }
24
25 std::wcout << L"------------------------------------------------------------------------" << std::endl;
26
27 Converted = { 0 };
28 RelativeName = { 0 };
29 FilePart = { 0 };
30
31 if (_RtlDosPathNameToNtPathName_U(DeviceNamespace, &Converted, &FilePart, &RelativeName))
32 {
33 std::wcout << L"[DeviceNamespace] (previous): " << DeviceNamespace << std::endl;
34 std::wcout << L"[DeviceNamespace] (converted): " << Converted.Buffer << std::endl;
35 std::wcout << L"[DeviceNamespace] FilePart: " << FilePart << std::endl;
36 std::wcout << L"[DeviceNamespace] RelativeName.Buffer: " << ((RelativeName.RelativeName.Buffer != NULL) ? RelativeName.RelativeName.Buffer : L"NULL") << std::endl;
37 std::wcout << L"[DeviceNamespace] ContainingDirectory: " << (void*)RelativeName.ContainingDirectory << std::endl;
38 std::wcout << L"[DeviceNamespace] CurDirRef->DirectoryHandle: " << ((RelativeName.CurDirRef != nullptr) ? (void*)RelativeName.CurDirRef->DirectoryHandle : L"NULL") << std::endl;
39 std::wcout << L"[DeviceNamespace] CurDirRef->ReferenceCount: " << ((RelativeName.CurDirRef != nullptr) ? RelativeName.CurDirRef->ReferenceCount : 0UL) << std::endl;
40 }
41
42 std::wcout << L"------------------------------------------------------------------------" << std::endl;
43
44 Converted = { 0 };
45 RelativeName = { 0 };
46 FilePart = { 0 };
47
48 if (_RtlDosPathNameToNtPathName_U(FileNamespace, &Converted, &FilePart, &RelativeName))
49 {
50 std::wcout << L"[FileNamespace] (previous): " << FileNamespace << std::endl;
51 std::wcout << L"[FileNamespace] (converted): " << Converted.Buffer << std::endl;
52 std::wcout << L"[FileNamespace] FilePart: " << FilePart << std::endl;
53 std::wcout << L"[FileNamespace] RelativeName.Buffer: " << ((RelativeName.RelativeName.Buffer != NULL) ? RelativeName.RelativeName.Buffer : L"NULL") << std::endl;
54 std::wcout << L"[FileNamespace] ContainingDirectory: " << (void*)RelativeName.ContainingDirectory << std::endl;
55 std::wcout << L"[FileNamespace] CurDirRef->DirectoryHandle: " << ((RelativeName.CurDirRef != nullptr) ? (void*)RelativeName.CurDirRef->DirectoryHandle : L"NULL") << std::endl;
56 std::wcout << L"[FileNamespace] CurDirRef->ReferenceCount: " << ((RelativeName.CurDirRef != nullptr) ? RelativeName.CurDirRef->ReferenceCount : 0UL) << std::endl;
57 }
58
59 std::wcout << L"------------------------------------------------------------------------" << std::endl;
60
61 Converted = { 0 };
62 RelativeName = { 0 };
63 FilePart = { 0 };
64
65 if (_RtlDosPathNameToNtPathName_U(DevicePath, &Converted, &FilePart, &RelativeName))
66 {
67 std::wcout << L"[DevicePath] (previous): " << DevicePath << std::endl;
68 std::wcout << L"[DevicePath] (converted): " << Converted.Buffer << std::endl;
69 std::wcout << L"[DevicePath] FilePart: " << FilePart << std::endl;
70 std::wcout << L"[DevicePath] RelativeName.Buffer: " << ((RelativeName.RelativeName.Buffer != NULL) ? RelativeName.RelativeName.Buffer : L"NULL") << std::endl;
71 std::wcout << L"[DevicePath] ContainingDirectory: " << (void*)RelativeName.ContainingDirectory << std::endl;
72 std::wcout << L"[DevicePath] CurDirRef->DirectoryHandle: " << ((RelativeName.CurDirRef != nullptr) ? (void*)RelativeName.CurDirRef->DirectoryHandle : L"NULL") << std::endl;
73 std::wcout << L"[DevicePath] CurDirRef->ReferenceCount: " << ((RelativeName.CurDirRef != nullptr) ? RelativeName.CurDirRef->ReferenceCount : 0UL) << std::endl;
74 }
75
76 std::wcout << L"------------------------------------------------------------------------" << std::endl;
77
78 Converted = { 0 };
79 RelativeName = { 0 };
80 FilePart = { 0 };
81
82 if (_RtlDosPathNameToNtPathName_U(ExtraTest, &Converted, &FilePart, &RelativeName))
83 {
84 std::wcout << L"[ExtraTest] (previous): " << ExtraTest << std::endl;
85 std::wcout << L"[ExtraTest] (converted): " << Converted.Buffer << std::endl;
86 std::wcout << L"[ExtraTest] FilePart: " << FilePart << std::endl;
87 std::wcout << L"[ExtraTest] RelativeName.Buffer: " << ((RelativeName.RelativeName.Buffer != NULL) ? RelativeName.RelativeName.Buffer : L"NULL") << std::endl;
88 std::wcout << L"[ExtraTest] ContainingDirectory: " << (void*)RelativeName.ContainingDirectory << std::endl;
89 std::wcout << L"[ExtraTest] CurDirRef->DirectoryHandle: " << ((RelativeName.CurDirRef != nullptr) ? (void*)RelativeName.CurDirRef->DirectoryHandle : L"NULL") << std::endl;
90 std::wcout << L"[ExtraTest] CurDirRef->ReferenceCount: " << ((RelativeName.CurDirRef != nullptr) ? RelativeName.CurDirRef->ReferenceCount : 0UL) << std::endl;
91 }
92}
结果:
笔记:
如果您使用相对路径,该功能仍然会成功。然而,输出将不仅包含 NT 路径,还将包含当前工作目录路径。检查最后的测试结果。
使用 RtlDosPathNameToNtPathName\_U\_WithStatus
此函数将返回 NTSTATUS 而不是 BOOLEAN。
它与上面的 RtlDosPathNameToNtPathName_U 完全相同,但是使用此版本,如果函数失败,您可以检索最后一个 NTSTATUS。
使用 RtlDosPathNameToNtPathName\_U\_WithStatus
此函数将返回 NTSTATUS 而不是 BOOLEAN。
使用 RtlDosLongPathNameToNtPathName\_U\_WithStatus
此函数将返回 NTSTATUS。
根据我的测试,这个函数返回了与上面完全相同的结果,但是使用这个版本,如果函数失败,您可以检索最后一个 NTSTATUS。
此功能仅在 Windows 10 Redstone 3 开始可用。我找不到任何特别之处。
根据我的猜测,它可以转换更长的路径,因此函数名称中的“long”。
使用 RtlDosPathNameToRelativeNtPathName\_U
此函数将返回一个 BOOLEAN。
根据我的测试,这个函数返回的结果与上面完全相同,所以我不确定它是怎么回事。 (随时发表评论)
使用 RtlDosPathNameToRelativeNtPathName\_U\_WithStatus
此函数将返回 NTSTATUS 而不是 BOOLEAN。
根据我的测试,这个函数返回的结果与上面完全相同,所以我不确定它是怎么回事。 (随时发表评论)
使用 RtlDosLongPathNameToRelativeNtPathName\_U\_WithStatus
此函数将返回 NTSTATUS 而不是 BOOLEAN。
这个函数再次返回相同的结果,也许我说错了。 (随时发表评论)
此功能仅从 Windows 10 Redstone 3 开始可用。
使用 RtlNtPathNameToDosPathName
该函数将返回一个 NTSTATUS。
这个函数本身很有趣,因为它的目的是扭转我们上面取得的成果。老实说,我很难找到一些文档,就像任何东西都只是为了测试它。
- Flags (在 - 可选): 我不知道它是干什么用的。
- Path (输入/输出 - 必需): 指向 RTL_UNICODE_STRING_BUFFER 初始化结构的指针。它还将包含执行后的结果。
- Disposition (在 - 可选): 我不知道它是干什么用的。
- FilePart (可选的): 我不知道它是干什么用的。
从我的测试中,您只能使用此功能转换路径而不是完整路径(例如到文件)。如果你尝试一个完整的路径,它不会转换它,但它仍然会正常执行 o.O
代码:
1{
2
3 RTL_UNICODE_STRING_BUFFER DosPath = { 0 };
4 PWSTR FilePart{};
5
6 UNICODE_STRING NtPath;
7 RtlInitUnicodeString(&NtPath, L"\\??\\C:\\WINDOWS\\system32\\drivers");
8
9 wchar_t* DosPathBuffer = NULL;
10 wchar_t* NtPathBuffer = NULL;
11
12 DosPathBuffer = (wchar_t*)new char[NtPath.Length + sizeof(wchar_t)];
13 NtPathBuffer = (wchar_t*)new char[NtPath.Length + sizeof(wchar_t)];
14
15 RtlZeroMemory(DosPathBuffer, NtPath.Length + sizeof(wchar_t));
16 RtlZeroMemory(NtPathBuffer, NtPath.Length + sizeof(wchar_t));
17 RtlCopyMemory(DosPathBuffer, NtPath.Buffer, NtPath.Length);
18 RtlCopyMemory(NtPathBuffer, NtPath.Buffer, NtPath.Length);
19
20 DosPath.ByteBuffer.Buffer = DosPathBuffer;
21 DosPath.ByteBuffer.StaticBuffer = NtPathBuffer;
22 DosPath.String.Buffer = NtPath.Buffer;
23 DosPath.String.Length = NtPath.Length;
24 DosPath.String.MaximumLength = NtPath.Length;
25 DosPath.ByteBuffer.Size = NtPath.Length;
26 DosPath.ByteBuffer.StaticSize = NtPath.Length;
27
28 pRtlNtPathNameToDosPathName _RtlNtPathNameToDosPathName = reinterpret_cast<pRtlNtPathNameToDosPathName>(GetProcAddress(GetModuleHandle(L"ntdll.dll"), "RtlNtPathNameToDosPathName"));
29
30 if (NT_SUCCESS(_RtlNtPathNameToDosPathName(0, &DosPath, NULL, &FilePart)))
31 {
32 //RtlCopyMemory(pszDosPath, ByteDosPathBuffer, wcslen(ByteDosPathBuffer) * sizeof(wchar_t));
33
34 std::wcout << L"[NtPath] (previous): " << NtPath.Buffer << std::endl;
35 std::wcout << L"[DosPathBuffer] (converted): " << DosPathBuffer << std::endl;
36 std::wcout << L"[NtPathBuffer] (untouched): " << NtPathBuffer << std::endl;
37 std::wcout << L"[NormalNamespace] FilePart: " << ((FilePart != nullptr) ? FilePart : L"NULL") << std::endl;
38 }
39
40 std::wcout << L"------------------------------------------------------------------------" << std::endl;
41
42}
结果:
我发布的代码示例令人困惑,但是让我试着澄清一下。发生的情况是该函数将替换所提供结构中的 NT 路径。
我在函数执行之前设置了一个断点:
然后在函数执行后,您可以看到更改:
所以你有了它,一个奇怪的函数,它可以将 NT 到 DOS 路径转换。
很抱歉,我无法提供有关这些功能的更多详细信息,但是请随时在下面发表评论并添加任何内容。
直到下一次。
评论