Si vous êtes ici en train de lire cet article, cela signifie que vous cherchez un moyen de convertir vos chemins DOS et/ou NT pour votre logiciel. Soyez assuré que c’est ce que vous apprendrez ici aujourd’hui !

Le problème avec chemins Windows est clair ; c’est tellement déroutant. Permettez-moi de répéter cela, si déroutant. Cela devient un problème lorsque vous travaillez sur votre incroyable logiciel et que vous devez convertir les chemins DOS et NT.

Dans cet article, je vais essayer de garder les choses courtes et douces, alors commençons.

Il existe des fonctions non documentées dans ntdll qui peuvent être utilisées pour différentes conversions. La liste de ces fonctions commence par Rtl* et se présente comme suit :

  • RtlDosPathNameToNtPathName_U (all windows)
  • RtlDosPathNameToNtPathName_U_WithStatus (all windows)
  • RtlDosLongPathNameToNtPathName_U_WithStatus (windows 10 Redstone 3)
  • RtlDosPathNameToRelativeNtPathName_U (all windows)
  • RtlDosPathNameToRelativeNtPathName_U_WithStatus (all windows)
  • RtlDosLongPathNameToRelativeNtPathName_U_WithStatus (windows 10 Redstone 3)
  • RtlNtPathNameToDosPathName (all windows; This particular function is quite a mystery because you cannot find definitions or information about it anywhere. However we will test all these functions accordingly and see their output.)

Les fonctions ci-dessus ont les mêmes paramètres d’entrée/sortie :

  • DosFileName (in – required): A constant string containing the DOS name of the target file or directory.
  • NtFileName (out – required): Return the NT path to a file or directory. Must use a UNICODE_STRING which does not need to be initialised.
  • FilePart (out – optional): At the output, the file name address in the generated path will be entered into the pointer. If this parameter is filled with NULL in the output, then only the directory name is specified.
  • RelativeName (out – optional): Pointer to a RTL_RELATIVE_NAME_U structure containing information about the current directory.

Pour notre jeu avec ces fonctions, j’ai défini 3 chemins :

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

En utilisant RtlDosPathNameToNtPathName\_U

Cette fonction renverra un BOOLEAN.

  • TRUE – The path transform operation completed successfully.
  • FALSE – The path conversion operation failed.

Code:

 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"; // 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}

Result:

RtlDosPathNameToNtPathName_U Output

Remarques:

Si vous utilisez un chemin relatif, la fonction réussira toujours. Cependant, la sortie contiendra non seulement le chemin NT, mais elle ajoutera également le chemin du répertoire de travail actuel. Vérifiez le dernier résultat de test.

En utilisant RtlDosPathNameToNtPathName\_U\_WithStatus

Cette fonction renverra NTSTATUS au lieu de BOOLEAN.

C’est exactement le même que celui ci-dessus RtlDosPathNameToNtPathName_U, cependant avec cette version vous pouvez récupérer le dernier NTSTATUS si la fonction a échoué.

En utilisant RtlDosLongPathNameToNtPathName\_U\_WithStatus

Cette fonction renverra NTSTATUS.

D’après mes tests, cette fonction a renvoyé exactement le même résultat que ci-dessus, mais avec cette version, vous pouvez récupérer le dernier NTSTATUS si la fonction a échoué.

Cette fonction n’est disponible qu’à partir de Windows 10 Redstone 3. Je ne trouve rien de spécial à ce sujet.

À mon avis, il peut convertir des chemins plus longs, d’où le “long” dans le nom de la fonction.

En utilisant RtlDosPathNameToRelativeNtPathName\_U

Cette fonction renverra un BOOLEAN.

D’après mes tests, cette fonction a renvoyé exactement le même résultat que ci-dessus, donc je ne sais pas quel est le problème. (n’hésitez pas à commenter)

En utilisant RtlDosPathNameToRelativeNtPathName\_U\_WithStatus

Cette fonction renverra NTSTATUS au lieu de BOOLEAN.

D’après mes tests, cette fonction a renvoyé exactement le même résultat que ci-dessus, donc je ne sais pas quel est le problème. (n’hésitez pas à commenter)

En utilisant RtlDosLongPathNameToRelativeNtPathName\_U\_WithStatus

Cette fonction renverra NTSTATUS au lieu de BOOLEAN.

Une fois de plus, cette fonction a renvoyé les mêmes résultats, peut-être que je l’appelle mal. (n’hésitez pas à commenter)

Cette fonction n’est disponible qu’à partir de Windows 10 Redstone 3.

En utilisant RtlNtPathNameToDosPathName

Cette fonction renverra un NTSTATUS.

La fonction elle-même est intéressante, car son but est d’inverser ce que nous avons réalisé ci-dessus. Pour être honnête, j’ai eu du mal à trouver de la documentation, comme quoi que ce soit juste pour le tester.

  • Flags (in – optional): I have no idea what it’s for.
  • Path (in/out – required): Pointer to RTL_UNICODE_STRING_BUFFER initialised structure. It will also contain the result after execution.
  • Disposition (in – optional): I have no idea what it’s for.
  • FilePart (out- optional): I have no idea what it’s for.

D’après mes tests, vous ne pouvez convertir que des chemins et non des chemins complets (par exemple vers un fichier) avec cette fonction. Si vous essayez un chemin complet, il ne le convertira pas, mais il s’exécutera toujours normalement 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}

Résultat:

RtlNtPathNameToDosPathName Output

L’exemple de code que j’ai posté est déroutant, mais laissez-moi essayer de clarifier les choses. Ce qui se passe, c’est que cette fonction remplacera les chemins NT dans la structure fournie.

J’ai défini un point d’arrêt avant l’exécution de la fonction :

RtlNtPathNameToDosPathName Debug 1

Et après l’exécution de la fonction, vous pouvez voir les changements :

RtlNtPathNameToDosPathName Debug 2

Donc voilà, une fonction bizarre qui fait la conversion de chemin NT vers DOS.

Je suis désolé de ne pas pouvoir donner plus de détails sur ces fonctions, mais n’hésitez pas à commenter ci-dessous avec tout ajout.

Jusqu’à la prochaine fois.