如果你在這裡閱讀這篇文章,這意味著你正在尋找一種方法來為你的軟件轉換你的 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)

上述函數具有相同的輸入/輸出參數:

  • DosFileName (在 - 必需): 包含目標文件或目錄的 DOS 名稱的常量字符串。
  • NtFileName (出——必填): 返回文件或目錄的 NT 路徑。必須使用不需要初始化的 UNICODE_STRING。
  • FilePart (出 - 可選): 在輸出時,生成路徑中的文件名地址將被輸入到指針中。如果此參數在輸出中用 NULL 填充,則僅指定目錄名稱。
  • RelativeName (出 - 可選): 指向包含當前目錄信息的 RTL_RELATIVE_NAME_U 結構的指針。
  • RtlNtPathNameToDosPathName (所有窗戶;這個特定的功能非常神秘,因為您無法在任何地方找到有關它的定義或信息。但是,我們將相應地測試所有這些函數並查看它們的輸出。)

為了我們使用這些功能,我定義了 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}

結果:

RtlDosPathNameToNtPathName_U Output

筆記:

如果您使用相對路徑,該功能仍然會成功。然而,輸出將不僅包含 NT 路徑,還將包含當前工作目錄路徑。檢查最後的測試結果。

使用 RtlDosPathNameToNtPathName\_U\_WithStatus

此函數將返回 NTSTATUS 而不是 BOOLEAN。

它與上面的 RtlDosPathNameToNtPathName_U 完全相同,但是使用此版本,如果函數失敗,您可以檢索最後一個 NTSTATUS。

使用 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}

結果:

RtlNtPathNameToDosPathName Output

我發布的代碼示例令人困惑,但是讓我試著澄清一下。發生的情況是此函數將替換提供的結構中的 NT 路徑。

我在函數執行之前設置了一個斷點:

RtlNtPathNameToDosPathName Debug 1

然後在函數執行後,您可以看到更改:

RtlNtPathNameToDosPathName Debug 2

所以你有了它,一個奇怪的函數,它可以將 NT 到 DOS 路徑轉換。

很抱歉,我無法提供有關這些功能的更多詳細信息,但是請隨時在下面發表評論並添加任何內容。

直到下一次。