Nếu bạn đang đọc bài viết này, điều đó có nghĩa là bạn đang tìm cách chuyển đổi đường dẫn DOS và/hoặc NT cho phần mềm của mình. Hãy yên tâm vì đó chính xác là những gì bạn sẽ học được ở đây hôm nay!
Vấn đề với đường dẫn Windows là rõ ràng: rất khó hiểu. Cho phép tôi nhắc lại, rất khó hiểu. Điều này trở thành vấn đề khi bạn đang làm việc trên phần mềm tuyệt vời của mình và cần chuyển đổi đường dẫn DOS và NT.
Trong bài viết này, tôi sẽ cố gắng trình bày ngắn gọn và súc tích, vậy hãy bắt đầu.
Có những hàm không được ghi nhận trong ntdll có thể được sử dụng cho các chuyển đổi khác nhau. Danh sách các hàm này bắt đầu bằng Rtl* và như sau:
- RtlDosPathNameToNtPathName_U (tất cả các phiên bản Windows)
- RtlDosPathNameToNtPathName_U_WithStatus (tất cả các phiên bản Windows)
- RtlDosLongPathNameToNtPathName_U_WithStatus (Windows 10 Redstone 3)
- RtlDosPathNameToRelativeNtPathName_U (tất cả các phiên bản Windows)
- RtlDosPathNameToRelativeNtPathName_U_WithStatus (tất cả các phiên bản Windows)
- RtlDosLongPathNameToRelativeNtPathName_U_WithStatus (Windows 10 Redstone 3)
- RtlNtPathNameToDosPathName (tất cả các phiên bản Windows; Hàm này khá bí ẩn vì bạn không thể tìm thấy định nghĩa hay thông tin về nó ở bất cứ đâu. Tuy nhiên, chúng ta sẽ kiểm tra tất cả các hàm này và xem kết quả đầu ra.)
Các hàm trên có cùng các tham số đầu vào/đầu ra:
- DosFileName (đầu vào - bắt buộc): Một chuỗi hằng chứa tên DOS của tệp hoặc thư mục đích.
- NtFileName (đầu ra - bắt buộc): Trả về đường dẫn NT tới một tệp hoặc thư mục. Phải sử dụng UNICODE_STRING mà không cần khởi tạo.
- FilePart (đầu ra - tùy chọn): Tại đầu ra, địa chỉ tên tệp trong đường dẫn được tạo sẽ được nhập vào con trỏ. Nếu tham số này được điền NULL ở đầu ra, thì chỉ có tên thư mục được chỉ định.
- RelativeName (đầu ra - tùy chọn): Con trỏ tới cấu trúc RTL_RELATIVE_NAME_U chứa thông tin về thư mục hiện tại.
Để thử nghiệm với các hàm này, tôi đã định nghĩa 3 đường dẫn:
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
Sử dụng RtlDosPathNameToNtPathName_U
Hàm này sẽ trả về BOOLEAN.
- TRUE - Thao tác chuyển đổi đường dẫn hoàn thành thành công.
- FALSE - Thao tác chuyển đổi đường dẫn thất bại.
Mã nguồn:
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}Kết quả:
Lưu ý:
Nếu bạn sử dụng đường dẫn tương đối, hàm vẫn sẽ thành công. Tuy nhiên, kết quả đầu ra sẽ không chỉ chứa đường dẫn NT mà còn thêm đường dẫn thư mục làm việc hiện tại vào phía trước. Hãy kiểm tra kết quả thử nghiệm cuối cùng.
Sử dụng RtlDosPathNameToNtPathName_U_WithStatus
Hàm này sẽ trả về NTSTATUS thay vì BOOLEAN.
Nó hoàn toàn giống với hàm RtlDosPathNameToNtPathName_U ở trên, tuy nhiên với phiên bản này bạn có thể lấy được NTSTATUS cuối cùng nếu hàm thất bại.
Sử dụng RtlDosLongPathNameToNtPathName_U_WithStatus
Hàm này sẽ trả về NTSTATUS.
Từ các thử nghiệm của tôi, hàm này trả về kết quả giống hệt như trên, tuy nhiên với phiên bản này bạn có thể lấy được NTSTATUS cuối cùng nếu hàm thất bại.
Hàm này chỉ có sẵn từ Windows 10 Redstone 3 trở đi. Tôi không thể tìm thấy điều gì đặc biệt về nó.
Phỏng đoán của tôi là nó có thể chuyển đổi các đường dẫn dài hơn, do đó có từ “long” trong tên hàm.
Sử dụng RtlDosPathNameToRelativeNtPathName_U
Hàm này sẽ trả về BOOLEAN.
Từ các thử nghiệm của tôi, hàm này trả về kết quả giống hệt như trên, nên tôi không chắc điểm khác biệt là gì. (hãy để lại bình luận nhé)
Sử dụng RtlDosPathNameToRelativeNtPathName_U_WithStatus
Hàm này sẽ trả về NTSTATUS thay vì BOOLEAN.
Từ các thử nghiệm của tôi, hàm này trả về kết quả giống hệt như trên, nên tôi không chắc điểm khác biệt là gì. (hãy để lại bình luận nhé)
Sử dụng RtlDosLongPathNameToRelativeNtPathName_U_WithStatus
Hàm này sẽ trả về NTSTATUS thay vì BOOLEAN.
Một lần nữa hàm này trả về cùng kết quả, có lẽ tôi đang gọi sai cách. (hãy để lại bình luận nhé)
Hàm này chỉ có sẵn từ Windows 10 Redstone 3 trở đi.
Sử dụng RtlNtPathNameToDosPathName
Hàm này sẽ trả về NTSTATUS.
Bản thân hàm này khá thú vị, vì mục đích của nó là đảo ngược những gì chúng ta đã đạt được ở trên. Thành thật mà nói, tôi gặp khó khăn khi tìm tài liệu, bất cứ thông tin gì chỉ để thử nghiệm nó.
- Flags (đầu vào - tùy chọn): Tôi không biết nó dùng để làm gì.
- Path (đầu vào/đầu ra - bắt buộc): Con trỏ tới cấu trúc RTL_UNICODE_STRING_BUFFER đã khởi tạo. Nó cũng sẽ chứa kết quả sau khi thực thi.
- Disposition (đầu vào - tùy chọn): Tôi không biết nó dùng để làm gì.
- FilePart (đầu ra - tùy chọn): Tôi không biết nó dùng để làm gì.
Từ các thử nghiệm của tôi, bạn chỉ có thể chuyển đổi đường dẫn chứ không phải đường dẫn đầy đủ (ví dụ: tới một tệp) với hàm này. Nếu bạn thử đường dẫn đầy đủ, nó sẽ không chuyển đổi, nhưng vẫn thực thi bình thường o.O
Mã nguồn:
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}Kết quả:
Ví dụ mã nguồn tôi đăng hơi khó hiểu, nhưng hãy để tôi cố gắng giải thích rõ hơn. Điều xảy ra là hàm này sẽ thay thế các đường dẫn NT trong cấu trúc được cung cấp.
Tôi đã đặt một breakpoint trước khi hàm thực thi:
Và sau khi hàm thực thi, bạn có thể thấy các thay đổi:
Vậy đó, một hàm kỳ lạ thực hiện chuyển đổi đường dẫn NT sang DOS.
Tôi xin lỗi vì không thể cung cấp thêm chi tiết về các hàm này, nhưng hãy thoải mái bình luận bên dưới với bất kỳ bổ sung nào.
Hẹn gặp lại lần sau.
Bình luận