この記事を読んでいるということは、ソフトウェアのためにDOSやNTパスを変換する方法を探しているということです。ここで今日まさにそれを学べるので安心してください!
Windowsパス の問題は明らかです。非常に混乱します。もう一度言います、非常に混乱します。素晴らしいソフトウェアを開発していて、DOSとNTパスを変換する必要がある時、これが問題になります。
この記事では簡潔にまとめるようにしますので、始めましょう。
ntdll内にはさまざまな変換に使用できる非公開の関数があります。これらの関数はRtl*で始まり、以下の通りです:
- RtlDosPathNameToNtPathName_U(全Windowsバージョン)
- RtlDosPathNameToNtPathName_U_WithStatus(全Windowsバージョン)
- RtlDosLongPathNameToNtPathName_U_WithStatus(Windows 10 Redstone 3)
- RtlDosPathNameToRelativeNtPathName_U(全Windowsバージョン)
- RtlDosPathNameToRelativeNtPathName_U_WithStatus(全Windowsバージョン)
- RtlDosLongPathNameToRelativeNtPathName_U_WithStatus(Windows 10 Redstone 3)
- RtlNtPathNameToDosPathName(全Windowsバージョン。この関数はかなり謎めいています。どこにも定義や情報が見つからないからです。しかし、これらすべての関数を適切にテストして出力を確認します。)
上記の関数は同じ入出力パラメータを持っています:
- DosFileName(入力 - 必須):対象ファイルまたはディレクトリのDOS名を含む定数文字列。
- NtFileName(出力 - 必須):ファイルまたはディレクトリへのNTパスを返します。初期化不要のUNICODE_STRINGを使用する必要があります。
- FilePart(出力 - オプション):出力時、生成されたパス内のファイル名アドレスがポインタに格納されます。出力でこのパラメータがNULLの場合、ディレクトリ名のみが指定されています。
- RelativeName(出力 - オプション):現在のディレクトリに関する情報を含むRTL_RELATIVE_NAME_U構造体へのポインタ。
これらの関数をテストするために、3つのパスを定義しました:
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
RtlDosPathNameToNtPathName_Uの使用
この関数はBOOLEANを返します。
- TRUE - パス変換操作が正常に完了しました。
- FALSE - パス変換操作が失敗しました。
コード:
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}結果:
注意:
相対パスを使用した場合でも、関数は正常に成功します。ただし、出力にはNTパスだけでなく、現在の作業ディレクトリのパスも先頭に追加されます。最後のテスト結果を確認してください。
RtlDosPathNameToNtPathName_U_WithStatusの使用
この関数はBOOLEANの代わりにNTSTATUSを返します。
上記のRtlDosPathNameToNtPathName_Uとまったく同じですが、このバージョンでは関数が失敗した場合に最後のNTSTATUSを取得できます。
RtlDosLongPathNameToNtPathName_U_WithStatusの使用
この関数はNTSTATUSを返します。
テストでは上記とまったく同じ結果が返されましたが、このバージョンでは関数が失敗した場合に最後のNTSTATUSを取得できます。
この関数はWindows 10 Redstone 3以降でのみ使用可能です。特別な点は見つかりませんでした。
推測では、より長いパスを変換できるのでしょう。関数名に「long」が含まれているのはそのためです。
RtlDosPathNameToRelativeNtPathName_Uの使用
この関数はBOOLEANを返します。
テストでは上記とまったく同じ結果が返されたので、何が違うのかよくわかりません。(コメントをお願いします)
RtlDosPathNameToRelativeNtPathName_U_WithStatusの使用
この関数はBOOLEANの代わりにNTSTATUSを返します。
テストでは上記とまったく同じ結果が返されたので、何が違うのかよくわかりません。(コメントをお願いします)
RtlDosLongPathNameToRelativeNtPathName_U_WithStatusの使用
この関数はBOOLEANの代わりにNTSTATUSを返します。
やはりこの関数も同じ結果を返しました。おそらく呼び出し方が間違っているのかもしれません。(コメントをお願いします)
この関数は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}結果:
投稿したコード例は分かりにくいですが、状況を整理してみましょう。この関数は提供された構造体内のNTパスを置き換えます。
関数実行前にブレークポイントを設定しました:
そして関数実行後に変更を確認できます:
以上です。NTからDOSパスへの変換を行う奇妙な関数です。
これらの関数について詳しい情報を提供できず申し訳ありませんが、追加情報があれば下のコメント欄にお気軽にどうぞ。
それではまた次回。
コメント