Dacă citești această postare, înseamnă că cauți o modalitate de a converti căile DOS și/sau NT pentru software-ul tău. Fii sigur că exact asta vei învăța aici astăzi!
Problema cu căile Windows este evidentă: este extrem de confuz. Să repet, extrem de confuz. Acest lucru devine o problemă când lucrezi la software-ul tău extraordinar și trebuie să convertești căi DOS și NT.
În acest articol voi încerca să fiu scurt și la obiect, așa că hai să începem.
Există funcții nedocumentate în ntdll care pot fi folosite pentru diferite conversii. Lista acestor funcții începe cu Rtl* și sunt următoarele:
- RtlDosPathNameToNtPathName_U (toate versiunile Windows)
- RtlDosPathNameToNtPathName_U_WithStatus (toate versiunile Windows)
- RtlDosLongPathNameToNtPathName_U_WithStatus (Windows 10 Redstone 3)
- RtlDosPathNameToRelativeNtPathName_U (toate versiunile Windows)
- RtlDosPathNameToRelativeNtPathName_U_WithStatus (toate versiunile Windows)
- RtlDosLongPathNameToRelativeNtPathName_U_WithStatus (Windows 10 Redstone 3)
- RtlNtPathNameToDosPathName (toate versiunile Windows; Această funcție este destul de misterioasă pentru că nu poți găsi definiții sau informații despre ea nicăieri. Totuși, vom testa toate aceste funcții corespunzător și vom vedea rezultatele lor.)
Funcțiile de mai sus au aceiași parametri de intrare/ieșire:
- DosFileName (intrare - obligatoriu): Un string constant care conține numele DOS al fișierului sau directorului țintă.
- NtFileName (ieșire - obligatoriu): Returnează calea NT către un fișier sau director. Trebuie să utilizezi un UNICODE_STRING care nu necesită inițializare.
- FilePart (ieșire - opțional): La ieșire, adresa numelui fișierului din calea generată va fi introdusă în pointer. Dacă acest parametru este completat cu NULL la ieșire, atunci este specificat doar numele directorului.
- RelativeName (ieșire - opțional): Pointer către o structură RTL_RELATIVE_NAME_U care conține informații despre directorul curent.
Pentru testarea acestor funcții am definit 3 căi:
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
Utilizarea RtlDosPathNameToNtPathName_U
Această funcție va returna un BOOLEAN.
- TRUE - Operația de transformare a căii s-a finalizat cu succes.
- FALSE - Operația de conversie a căii a eșuat.
Cod:
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}Rezultat:
Note:
Dacă folosești o cale relativă, funcția va reuși în continuare. Totuși, rezultatul va conține nu doar calea NT, ci și calea directorului curent de lucru adăugată în față. Verifică ultimul rezultat al testului.
Utilizarea RtlDosPathNameToNtPathName_U_WithStatus
Această funcție va returna NTSTATUS în loc de BOOLEAN.
Este exact la fel ca cea de mai sus RtlDosPathNameToNtPathName_U, dar cu această versiune poți obține ultimul NTSTATUS dacă funcția a eșuat.
Utilizarea RtlDosLongPathNameToNtPathName_U_WithStatus
Această funcție va returna NTSTATUS.
Din testele mele, această funcție a returnat exact același rezultat ca mai sus, dar cu această versiune poți obține ultimul NTSTATUS dacă funcția a eșuat.
Această funcție este disponibilă doar începând cu Windows 10 Redstone 3. Nu pot găsi nimic special despre ea.
Presupunerea mea este că poate converti căi mai lungi, de unde și “long” în numele funcției.
Utilizarea RtlDosPathNameToRelativeNtPathName_U
Această funcție va returna un BOOLEAN.
Din testele mele, această funcție a returnat exact același rezultat ca mai sus, deci nu sunt sigur care este diferența. (nu ezitați să comentați)
Utilizarea RtlDosPathNameToRelativeNtPathName_U_WithStatus
Această funcție va returna NTSTATUS în loc de BOOLEAN.
Din testele mele, această funcție a returnat exact același rezultat ca mai sus, deci nu sunt sigur care este diferența. (nu ezitați să comentați)
Utilizarea RtlDosLongPathNameToRelativeNtPathName_U_WithStatus
Această funcție va returna NTSTATUS în loc de BOOLEAN.
Din nou, această funcție a returnat aceleași rezultate, poate o apelez greșit. (nu ezitați să comentați)
Această funcție este disponibilă doar începând cu Windows 10 Redstone 3.
Utilizarea RtlNtPathNameToDosPathName
Această funcție va returna un NTSTATUS.
Funcția în sine este interesantă, deoarece scopul ei este să inverseze ceea ce am realizat mai sus. Sincer, am avut dificultăți în a găsi documentație, orice, doar ca să o testez.
- Flags (intrare - opțional): Nu am idee la ce servește.
- Path (intrare/ieșire - obligatoriu): Pointer către o structură RTL_UNICODE_STRING_BUFFER inițializată. Va conține și rezultatul după execuție.
- Disposition (intrare - opțional): Nu am idee la ce servește.
- FilePart (ieșire - opțional): Nu am idee la ce servește.
Din testele mele, cu această funcție poți converti doar căi și nu căi complete (de ex. către un fișier). Dacă încerci o cale completă, nu o va converti, dar se va executa normal o.O
Cod:
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}Rezultat:
Exemplul de cod pe care l-am postat este confuz, dar haideți să clarific lucrurile. Ce se întâmplă este că această funcție va înlocui căile NT din structura furnizată.
Am setat un breakpoint înainte de execuția funcției:
Și apoi după execuția funcției poți vedea modificările:
Deci iată, o funcție ciudată care face conversia de la NT la DOS path.
Îmi pare rău că nu pot oferi mai multe detalii despre aceste funcții, dar nu ezitați să comentați mai jos cu orice completări.
Pe data viitoare.
Comentarii