إذا كنت تقرأ هذا المنشور هنا ، فهذا يعني أنك تبحث عن طريقة لتحويل مسارات DOS و / أو NT لبرنامجك. كن مطمئنًا أن هذا ما سوف تتعلمه هنا اليوم!

مشكلة [مسارات Windows] (https://docs.microsoft.com/en-us/dotnet/standard/io/file-path-formats ) واضحة ؛ إنه محير للغاية. اسمحوا لي أن أكرر ذلك ، محير للغاية. تصبح هذه مشكلة عندما تعمل على برنامج رائع وتحتاج إلى تحويل مسارات DOS و NT.

سأحاول في هذه المقالة أن أجعل الأشياء قصيرة ولطيفة ، فلنبدأ.

هناك وظائف غير موثقة داخل ntdll يمكن استخدامها لتحويلات مختلفة. قائمة هذه الوظائف تبدأ بـ Rtl * وهي كالتالي:

  • 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 (جميع النوافذ هذه الوظيفة المعينة هي لغزا تماما لأنه لا يمكنك العثور على تعريفات أو معلومات عنها في أي مكان. ومع ذلك ، سنختبر كل هذه الوظائف وفقًا لذلك ونرى نتائجها.)

الوظائف المذكورة أعلاه لها نفس معلمات الإدخال / الإخراج:

  • ** DosFileName ** (in - required): سلسلة ثابتة تحتوي على اسم DOS للملف الهدف أو الدليل.
  • ** NtFileName ** (out - required): إعادة مسار NT إلى ملف أو دليل. يجب استخدام UNICODE \ _STRING التي لا تحتاج إلى التهيئة.
  • ** FilePart ** (خارجي - اختياري): عند الإخراج ، سيتم إدخال عنوان اسم الملف في المسار الذي تم إنشاؤه في المؤشر. إذا تم ملء هذه المعلمة بـ NULL في الإخراج ، فسيتم تحديد اسم الدليل فقط.
  • ** RelativeName ** (خارجي - اختياري): مؤشر إلى بنية RTL \ _RELATIVE \ _NAME \ _U تحتوي على معلومات حول الدليل الحالي.

من أجل لعبنا بهذه الوظائف ، قمت بتحديد 3 مسارات:

1static const wchar_t NormalNamespace[MAX_PATH] = L"C:\\Windows\\System32\\csrss.exe"; // مساحة اسم Win32 العادية
2static const wchar_t DeviceNamespace[MAX_PATH] = L"\\\\.\\C:\\Windows\\System32\\csrss.exe"; // مساحة اسم جهاز Win32
3static const wchar_t FileNamespace[MAX_PATH] = L"\\\\?\\C:\\Windows\\System32\\csrss.exe"; // مساحة اسم جهاز Win32
4static const wchar_t DevicePath[MAX_PATH] = L"\\Device\\HarddiskVolume1\\Windows\\System32\\csrss.exe"; // مسار جهاز Win32
5static const wchar_t ExtraTest[MAX_PATH] = L"directory.name\\file.name"; // مسار نسبي

استخدام RtlDosPathNameToNtPathName_U

ستعيد هذه الوظيفة قيمة BOOLEAN.

  • TRUE - اكتملت عملية تحويل المسار بنجاح.
  • خطأ - فشلت عملية تحويل المسار.

شفرة:

 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}

نتيجة:

RtlDosPathNameToNtPathName_U Output

ملحوظات:

إذا كنت مسارًا نسبيًا ، فستظل الوظيفة ناجحة. ومع ذلك ، لن يحتوي الإخراج على مسار NT فحسب ، بل سيضيف أيضًا مسار دليل العمل الحالي مسبقًا. تحقق من نتيجة الاختبار الأخيرة.

استخدام RtlDosPathNameToNtPathName_U_WithStatus

ستعيد هذه الوظيفة NTSTATUS بدلاً من BOOLEAN.

إنه بالضبط نفس الاسم أعلاه ** RtlDosPathNameToNtPathName_U ** ، ولكن مع هذا الإصدار يمكنك استرداد NTSTATUS الأخير إذا فشلت الوظيفة.

استخدام RtlDosLongPathNameToNtPathName_U_WithStatus

ستعيد هذه الوظيفة NTSTATUS.

من الاختبارات التي أجريتها ، أعادت هذه الوظيفة النتيجة نفسها تمامًا كما هو مذكور أعلاه ، ولكن مع هذا الإصدار ، يمكنك استرداد آخر NTSTATUS إذا فشلت الوظيفة.

هذه الوظيفة متاحة فقط عند بدء تشغيل Windows 10 Redstone 3. لا يمكنني العثور على أي شيء مميز عنها.

من خلال تخميني ، يمكن تحويل المسارات الأطول ، ومن هنا جاءت كلمة “طويلة” في اسم الوظيفة.

استخدام RtlDosPathNameToRelativeNtPathName_U

ستعيد هذه الوظيفة قيمة BOOLEAN.

من الاختبارات التي أجريتها ، عادت هذه الوظيفة إلى النتيجة نفسها تمامًا كما هو مذكور أعلاه ، لذلك لست متأكدًا من الصفقة معها. (لا تتردد في التعليق)

استخدام RtlDosPathNameToRelativeNtPathName_U_WithStatus

ستعيد هذه الوظيفة NTSTATUS بدلاً من BOOLEAN.

من الاختبارات التي أجريتها ، عادت هذه الوظيفة إلى النتيجة نفسها تمامًا كما هو مذكور أعلاه ، لذلك لست متأكدًا من الصفقة معها. (لا تتردد في التعليق)

استخدام RtlDosLongPathNameToRelativeNtPathName_U_WithStatus

ستعيد هذه الوظيفة NTSTATUS بدلاً من BOOLEAN.

مرة أخرى ، عادت هذه الوظيفة إلى نفس النتائج ، ربما أصفها بأنها خاطئة. (لا تتردد في التعليق)

هذه الوظيفة متاحة فقط عند بدء تشغيل Windows 10 Redstone 3.

Using RtlNtPathNameToDosPathName

ستعيد هذه الوظيفة NTSTATUS.

الوظيفة نفسها مثيرة للاهتمام ، حيث أن الغرض منها هو عكس ما حققناه أعلاه. لأكون صادقًا ، واجهت صعوبة في العثور على بعض الوثائق ، مثل أي شيء على الإطلاق لمجرد اختباره.

  • ** الإشارات (في - اختياري): ** ليس لدي أي فكرة عن الغرض من ذلك.
  • ** المسار (الداخل / الخارج - مطلوب): ** المؤشر إلى RTL_UNICODE_STRING_BUFFER الهيكل المهيأ. سيحتوي أيضًا على النتيجة بعد التنفيذ.
  • ** الترتيب (في - اختياري): ** ليس لدي أي فكرة عن الغرض منه.
  • ** 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}

نتيجة:

RtlNtPathNameToDosPathName Output

مثال الكود الذي نشرته محيرًا ، لكن دعني أحاول تنقية الهواء. ما يحدث هو أن هذه الوظيفة ستحل محل مسارات NT في الهيكل المقدم.

لقد قمت بتعيين نقطة توقف قبل تنفيذ الوظيفة:

RtlNtPathNameToDosPathName Debug 1

وبعد تنفيذ الوظيفة ، يمكنك رؤية التغييرات:

RtlNtPathNameToDosPathName Debug 2

إذاً لديك ، وظيفة غريبة تقوم بتحويل مسار NT إلى DOS.

أنا آسف لأنني لا أستطيع تقديم المزيد من التفاصيل حول هذه الوظائف ، ولكن لا تتردد في التعليق أدناه مع أي إضافات.

حتى المرة القادمة.