Wenn Sie diesen Beitrag lesen, suchen Sie nach einer Möglichkeit, Ihre DOS- und/oder NT-Pfade für Ihre Software zu konvertieren. Seien Sie versichert, dass Sie genau das hier heute lernen werden!
Das Problem mit Windows-Pfaden ist offensichtlich: Es ist so verwirrend. Lassen Sie mich das wiederholen, so verwirrend. Dies wird zum Problem, wenn Sie an Ihrer großartigen Software arbeiten und DOS- und NT-Pfade konvertieren müssen.
In diesem Artikel werde ich versuchen, die Dinge kurz und knapp zu halten, also legen wir los.
Es gibt undokumentierte Funktionen in ntdll, die für verschiedene Konvertierungen verwendet werden können. Die Liste dieser Funktionen beginnt mit Rtl* und lautet wie folgt:
- RtlDosPathNameToNtPathName_U (alle Windows-Versionen)
- RtlDosPathNameToNtPathName_U_WithStatus (alle Windows-Versionen)
- RtlDosLongPathNameToNtPathName_U_WithStatus (Windows 10 Redstone 3)
- RtlDosPathNameToRelativeNtPathName_U (alle Windows-Versionen)
- RtlDosPathNameToRelativeNtPathName_U_WithStatus (alle Windows-Versionen)
- RtlDosLongPathNameToRelativeNtPathName_U_WithStatus (Windows 10 Redstone 3)
- RtlNtPathNameToDosPathName (alle Windows-Versionen; Diese Funktion ist ziemlich mysteriös, da man nirgends Definitionen oder Informationen darüber finden kann. Wir werden jedoch alle diese Funktionen entsprechend testen und ihre Ausgabe überprüfen.)
Die oben genannten Funktionen haben die gleichen Ein-/Ausgabeparameter:
- DosFileName (Eingabe - erforderlich): Ein konstanter String, der den DOS-Namen der Zieldatei oder des Verzeichnisses enthält.
- NtFileName (Ausgabe - erforderlich): Gibt den NT-Pfad zu einer Datei oder einem Verzeichnis zurück. Muss einen UNICODE_STRING verwenden, der nicht initialisiert werden muss.
- FilePart (Ausgabe - optional): Bei der Ausgabe wird die Dateinamenadresse im generierten Pfad in den Zeiger eingetragen. Wenn dieser Parameter bei der Ausgabe mit NULL gefüllt wird, wird nur der Verzeichnisname angegeben.
- RelativeName (Ausgabe - optional): Zeiger auf eine RTL_RELATIVE_NAME_U-Struktur, die Informationen über das aktuelle Verzeichnis enthält.
Für unsere Tests mit diesen Funktionen habe ich 3 Pfade definiert:
1static const wchar_t NormalNamespace[MAX_PATH] = L"C:\\Windows\\System32\\csrss.exe"; // Normal Win32 namespace
2static const wchar_t DeviceNamespace[MAX_PATH] = L"\\\\.\\C:\\Windows\\System32\\csrss.exe"; // Win32 Device Namespace
3static const wchar_t FileNamespace[MAX_PATH] = L"\\\\?\\C:\\Windows\\System32\\csrss.exe"; // Win32 Device Namespace
4static const wchar_t DevicePath[MAX_PATH] = L"\\Device\\HarddiskVolume1\\Windows\\System32\\csrss.exe"; // Win32 Device Path
5static const wchar_t ExtraTest[MAX_PATH] = L"directory.name\\file.name"; // Relative Path
Verwendung von RtlDosPathNameToNtPathName_U
Diese Funktion gibt einen BOOLEAN zurück.
- TRUE - Die Pfadumwandlung wurde erfolgreich abgeschlossen.
- FALSE - Die Pfadkonvertierung ist fehlgeschlagen.
Code:
1{
2UNICODE_STRING Converted{};
3RTL_RELATIVE_NAME_U RelativeName{};
4PWSTR FilePart{};
5
6 static const wchar_t NormalNamespace[MAX_PATH] = L"C:\\Windows\\System32\\csrss.exe"; // Normal Win32 namespace
7 static const wchar_t DeviceNamespace[MAX_PATH] = L"\\\\.\\C:\\Windows\\System32\\csrss.exe"; // Win32 Device Namespace
8 static const wchar_t FileNamespace[MAX_PATH] = L"\\\\?\\C:\\Windows\\System32\\csrss.exe"; // Win32 Device Namespace
9 static const wchar_t DevicePath[MAX_PATH] = L"\\Device\\HarddiskVolume1\\Windows\\System32\\csrss.exe"; // Win32 Device Path
10 static const wchar_t ExtraTest[MAX_PATH] = L"directory.name\\file.name"; // Relative Path
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
93}Ergebnis:
Hinweise:
Bei einem relativen Pfad wird die Funktion trotzdem erfolgreich ausgeführt. Die Ausgabe enthält jedoch nicht nur den NT-Pfad, sondern stellt auch das aktuelle Arbeitsverzeichnis voran. Überprüfen Sie das letzte Testergebnis.
Verwendung von RtlDosPathNameToNtPathName_U_WithStatus
Diese Funktion gibt NTSTATUS anstelle von BOOLEAN zurück.
Sie ist genau gleich wie die obige RtlDosPathNameToNtPathName_U, jedoch können Sie mit dieser Version den letzten NTSTATUS abrufen, wenn die Funktion fehlgeschlagen ist.
Verwendung von RtlDosLongPathNameToNtPathName_U_WithStatus
Diese Funktion gibt NTSTATUS zurück.
Aus meinen Tests hat diese Funktion genau dasselbe Ergebnis wie oben zurückgegeben, jedoch können Sie mit dieser Version den letzten NTSTATUS abrufen, wenn die Funktion fehlgeschlagen ist.
Diese Funktion ist erst ab Windows 10 Redstone 3 verfügbar. Ich kann nichts Besonderes darüber finden.
Meiner Vermutung nach kann sie längere Pfade konvertieren, daher das “Long” im Funktionsnamen.
Verwendung von RtlDosPathNameToRelativeNtPathName_U
Diese Funktion gibt einen BOOLEAN zurück.
Aus meinen Tests hat diese Funktion genau dasselbe Ergebnis wie oben zurückgegeben, daher bin ich mir nicht sicher, was es damit auf sich hat. (kommentieren Sie gerne)
Verwendung von RtlDosPathNameToRelativeNtPathName_U_WithStatus
Diese Funktion gibt NTSTATUS anstelle von BOOLEAN zurück.
Aus meinen Tests hat diese Funktion genau dasselbe Ergebnis wie oben zurückgegeben, daher bin ich mir nicht sicher, was es damit auf sich hat. (kommentieren Sie gerne)
Verwendung von RtlDosLongPathNameToRelativeNtPathName_U_WithStatus
Diese Funktion gibt NTSTATUS anstelle von BOOLEAN zurück.
Diese Funktion hat wiederum die gleichen Ergebnisse zurückgegeben, vielleicht rufe ich sie falsch auf. (kommentieren Sie gerne)
Diese Funktion ist erst ab Windows 10 Redstone 3 verfügbar.
Verwendung von RtlNtPathNameToDosPathName
Diese Funktion gibt einen NTSTATUS zurück.
Die Funktion selbst ist interessant, da ihr Zweck darin besteht, das oben Erreichte umzukehren. Ehrlich gesagt hatte ich große Schwierigkeiten, Dokumentation zu finden, irgendetwas, nur um sie zu testen.
- Flags (Eingabe - optional): Ich habe keine Ahnung, wofür es ist.
- Path (Ein-/Ausgabe - erforderlich): Zeiger auf eine initialisierte RTL_UNICODE_STRING_BUFFER-Struktur. Sie enthält nach der Ausführung auch das Ergebnis.
- Disposition (Eingabe - optional): Ich habe keine Ahnung, wofür es ist.
- FilePart (Ausgabe - optional): Ich habe keine Ahnung, wofür es ist.
Aus meinen Tests können Sie mit dieser Funktion nur Pfade und keine vollständigen Pfade (z.B. zu einer Datei) konvertieren. Wenn Sie einen vollständigen Pfad versuchen, wird er nicht konvertiert, aber die Funktion wird trotzdem normal ausgeführt o.O
Code:
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}Ergebnis:
Das Code-Beispiel, das ich gepostet habe, ist verwirrend, aber lassen Sie mich versuchen, die Sache zu klären. Was passiert, ist, dass diese Funktion die NT-Pfade in der bereitgestellten Struktur ersetzt.
Ich habe einen Breakpoint gesetzt, bevor die Funktion ausgeführt wird:
Und nach der Ausführung der Funktion können Sie die Änderungen sehen:
Da haben Sie es also, eine merkwürdige Funktion, die NT- zu DOS-Pfadkonvertierung durchführt.
Es tut mir leid, dass ich nicht mehr Details zu diesen Funktionen geben kann, aber bitte kommentieren Sie unten mit etwaigen Ergänzungen.
Bis zum nächsten Mal.
Kommentare