Se stai leggendo questo post, significa che stai cercando un modo per convertire i tuoi percorsi DOS e/o NT per il tuo software. Stai tranquillo, è esattamente quello che imparerai qui oggi!
Il problema con i percorsi Windows è evidente: è molto confuso. Lasciatemelo ripetere, molto confuso. Questo diventa un problema quando lavori al tuo fantastico software e devi convertire percorsi DOS e NT.
In questo articolo cercherò di essere breve e conciso, quindi iniziamo.
Ci sono funzioni non documentate in ntdll che possono essere usate per diverse conversioni. L’elenco di queste funzioni inizia con Rtl* e sono le seguenti:
- RtlDosPathNameToNtPathName_U (tutte le versioni di Windows)
- RtlDosPathNameToNtPathName_U_WithStatus (tutte le versioni di Windows)
- RtlDosLongPathNameToNtPathName_U_WithStatus (Windows 10 Redstone 3)
- RtlDosPathNameToRelativeNtPathName_U (tutte le versioni di Windows)
- RtlDosPathNameToRelativeNtPathName_U_WithStatus (tutte le versioni di Windows)
- RtlDosLongPathNameToRelativeNtPathName_U_WithStatus (Windows 10 Redstone 3)
- RtlNtPathNameToDosPathName (tutte le versioni di Windows; Questa funzione è piuttosto misteriosa perché non è possibile trovare definizioni o informazioni al riguardo da nessuna parte. Tuttavia testeremo tutte queste funzioni di conseguenza e vedremo il loro output.)
Le funzioni sopra elencate hanno gli stessi parametri di input/output:
- DosFileName (in - obbligatorio): Una stringa costante contenente il nome DOS del file o della directory di destinazione.
- NtFileName (out - obbligatorio): Restituisce il percorso NT di un file o directory. Deve usare un UNICODE_STRING che non necessita di essere inizializzato.
- FilePart (out - opzionale): Nell’output, l’indirizzo del nome file nel percorso generato verrà inserito nel puntatore. Se questo parametro è riempito con NULL nell’output, allora è specificato solo il nome della directory.
- RelativeName (out - opzionale): Puntatore a una struttura RTL_RELATIVE_NAME_U contenente informazioni sulla directory corrente.
Per i nostri test con queste funzioni ho definito 3 percorsi:
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
Utilizzo di RtlDosPathNameToNtPathName_U
Questa funzione restituirà un BOOLEAN.
- TRUE - L’operazione di trasformazione del percorso è stata completata con successo.
- FALSE - L’operazione di conversione del percorso è fallita.
Codice:
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}Risultato:
Note:
Se usi un percorso relativo, la funzione avrà comunque successo. Tuttavia l’output conterrà non solo il percorso NT ma anche il percorso della directory di lavoro corrente preposto. Controlla l’ultimo risultato del test.
Utilizzo di RtlDosPathNameToNtPathName_U_WithStatus
Questa funzione restituirà NTSTATUS invece di BOOLEAN.
È esattamente uguale alla precedente RtlDosPathNameToNtPathName_U, tuttavia con questa versione puoi recuperare l’ultimo NTSTATUS se la funzione è fallita.
Utilizzo di RtlDosLongPathNameToNtPathName_U_WithStatus
Questa funzione restituirà NTSTATUS.
Dai miei test questa funzione ha restituito esattamente lo stesso risultato di sopra, tuttavia con questa versione puoi recuperare l’ultimo NTSTATUS se la funzione è fallita.
Questa funzione è disponibile solo a partire da Windows 10 Redstone 3. Non riesco a trovare nulla di speciale al riguardo.
La mia ipotesi è che possa convertire percorsi più lunghi, da cui il “long” nel nome della funzione.
Utilizzo di RtlDosPathNameToRelativeNtPathName_U
Questa funzione restituirà un BOOLEAN.
Dai miei test questa funzione ha restituito esattamente lo stesso risultato di sopra, quindi non sono sicuro di quale sia la differenza. (sentitevi liberi di commentare)
Utilizzo di RtlDosPathNameToRelativeNtPathName_U_WithStatus
Questa funzione restituirà NTSTATUS invece di BOOLEAN.
Dai miei test questa funzione ha restituito esattamente lo stesso risultato di sopra, quindi non sono sicuro di quale sia la differenza. (sentitevi liberi di commentare)
Utilizzo di RtlDosLongPathNameToRelativeNtPathName_U_WithStatus
Questa funzione restituirà NTSTATUS invece di BOOLEAN.
Ancora una volta questa funzione ha restituito gli stessi risultati, forse la sto chiamando in modo errato. (sentitevi liberi di commentare)
Questa funzione è disponibile solo a partire da Windows 10 Redstone 3.
Utilizzo di RtlNtPathNameToDosPathName
Questa funzione restituirà un NTSTATUS.
La funzione in sé è interessante, poiché il suo scopo è invertire ciò che abbiamo ottenuto sopra. Ad essere onesti ho avuto difficoltà a trovare della documentazione, qualsiasi cosa, solo per testarla.
- Flags (in - opzionale): Non ho idea a cosa serva.
- Path (in/out - obbligatorio): Puntatore a una struttura RTL_UNICODE_STRING_BUFFER inizializzata. Conterrà anche il risultato dopo l’esecuzione.
- Disposition (in - opzionale): Non ho idea a cosa serva.
- FilePart (out - opzionale): Non ho idea a cosa serva.
Dai miei test puoi convertire solo percorsi e non percorsi completi (es. verso un file) con questa funzione. Se provi un percorso completo non lo convertirà, ma si eseguirà normalmente o.O
Codice:
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}Risultato:
L’esempio di codice che ho pubblicato è confuso, tuttavia lasciatemi provare a chiarire la situazione. Quello che succede è che questa funzione sostituirà i percorsi NT nella struttura fornita.
Ho impostato un breakpoint prima dell’esecuzione della funzione:
E poi dopo l’esecuzione della funzione puoi vedere le modifiche:
Ecco fatto, una funzione strana che esegue la conversione da percorso NT a DOS.
Mi dispiace di non poter fornire maggiori dettagli su queste funzioni, tuttavia sentitevi liberi di commentare qui sotto con eventuali aggiunte.
Alla prossima.
Commenti