9

我正在报告从本机系统 API 收集的一些信息。(我知道这很糟糕......但我得到了我无法获得的信息,如果/当那个时间到来时,我不得不更新我的应用程序没有什么问题。)

本机 API 返回本机路径名,如ob、 ie\SystemRoot\System32\Ntoskrnl.exe或所见\??\C:\Program Files\VMWare Workstation\vstor-ws60.sys

我可以替换常见的前缀,即

std::wstring NtPathToWin32Path( std::wstring ntPath )
{
    if (boost::starts_with(ntPath, L"\\\\?\\"))
    {
        ntPath.erase(ntPath.begin(), ntPath.begin() + 4);
        return ntPath;
    }
    if (boost::starts_with(ntPath, L"\\??\\"))
    {
        ntPath.erase(ntPath.begin(), ntPath.begin() + 4);
    }
    if (boost::starts_with(ntPath, L"\\"))
    {
        ntPath.erase(ntPath.begin(), ntPath.begin() + 1);
    }
    if (boost::istarts_with(ntPath, L"globalroot\\"))
    {
        ntPath.erase(ntPath.begin(), ntPath.begin() + 11);
    }
    if (boost::istarts_with(ntPath, L"systemroot"))
    {
        ntPath.replace(ntPath.begin(), ntPath.begin() + 10, GetWindowsPath());
    }
    if (boost::istarts_with(ntPath, L"windows"))
    {
        ntPath.replace(ntPath.begin(), ntPath.begin() + 7, GetWindowsPath());
    }
    return ntPath;
}

TEST(Win32Path, NtPathDoubleQuestions)
{
    ASSERT_EQ(L"C:\\Example", NtPathToWin32Path(L"\\??\\C:\\Example"));
}

TEST(Win32Path, NtPathUncBegin)
{
    ASSERT_EQ(L"C:\\Example", NtPathToWin32Path(L"\\\\?\\C:\\Example"));
}

TEST(Win32Path, NtPathWindowsStart)
{
    ASSERT_EQ(GetCombinedPath(GetWindowsPath(), L"Hello\\World"), NtPathToWin32Path(L"\\Windows\\Hello\\World"));
}

TEST(Win32Path, NtPathSystemrootStart)
{
    ASSERT_EQ(GetCombinedPath(GetWindowsPath(), L"Hello\\World"), NtPathToWin32Path(L"\\SystemRoot\\Hello\\World"));
}

TEST(Win32Path, NtPathGlobalRootSystemRoot)
{
    ASSERT_EQ(GetCombinedPath(GetWindowsPath(), L"Hello\\World"), NtPathToWin32Path(L"\\globalroot\\SystemRoot\\Hello\\World"));
}

但是如果没有一些 API,无论是原生的还是其他的,我都会感到非常惊讶,它将这些 API 转换为 Win32 路径名。这样的 API 存在吗?

4

6 回答 6

8

我们在生产代码中执行此操作。据我所知,没有 API(公共或私有)可以处理这个问题。我们只是用一些前缀做一些字符串比较,它对我们有用。

显然在 ntdll.dll 中有一个名为 RtlNtPathNameToDosPathName() 的函数(随 XP 引入?),但我不知道它的作用;不过,我猜它与\Device\Harddisk0 之类的东西有更多关系。

不过,我不确定是否真的需要这样的功能。Win32 将路径(在 CreateFile 等意义上)传递给 NT;NT 不会将路径传递给 Win32。所以 ntdll.dll 真的不需要从 NT 路径转到 Win32 路径。在某些 NT 查询函数返回完整路径的罕见情况下,任何转换函数都可能在 Win32 dll 内部(例如,未导出)。我什至不知道他们是否打扰,因为像 GetModuleFileName() 这样的东西只会返回用于加载图像的任何路径。我想这只是一个泄漏的抽象。

于 2010-12-15T02:29:45.037 回答
5

这是你可以尝试的东西。首先使用NtCreateFile打开文件、卷等进行读取。然后使用返回的 HANDLE 获取完整路径,如此所述。

于 2010-12-15T16:15:19.740 回答
4

这有点晚了,但我仍然会发布我的答案,因为即使在今天这是一个非常好的问题!

我将分享我测试并用于将 NT 转换为 DOS 路径的功能之一。就我而言,我还必须从 ANSI 转换为 UNICODE,所以这是一个小小的好处,您可以看到并了解如何做到这一点。

所有这些代码都可以在用户模式下使用,所以我们需要先准备一些东西。

定义和结构:

typedef NTSTATUS(WINAPI* pRtlAnsiStringToUnicodeString)(PUNICODE_STRING, PANSI_STRING, BOOL);

typedef struct _RTL_BUFFER {
    PUCHAR    Buffer;
    PUCHAR    StaticBuffer;
    SIZE_T    Size;
    SIZE_T    StaticSize;
    SIZE_T    ReservedForAllocatedSize; // for future doubling
    PVOID     ReservedForIMalloc; // for future pluggable growth
} RTL_BUFFER, * PRTL_BUFFER;

typedef struct _RTL_UNICODE_STRING_BUFFER {
    UNICODE_STRING String;
    RTL_BUFFER     ByteBuffer;
    UCHAR          MinimumStaticBufferForTerminalNul[sizeof(WCHAR)];
} RTL_UNICODE_STRING_BUFFER, * PRTL_UNICODE_STRING_BUFFER;

#define RTL_NT_PATH_NAME_TO_DOS_PATH_NAME_AMBIGUOUS   (0x00000001)
#define RTL_NT_PATH_NAME_TO_DOS_PATH_NAME_UNC         (0x00000002)
#define RTL_NT_PATH_NAME_TO_DOS_PATH_NAME_DRIVE       (0x00000003)
#define RTL_NT_PATH_NAME_TO_DOS_PATH_NAME_ALREADY_DOS (0x00000004)

typedef NTSTATUS(WINAPI* pRtlNtPathNameToDosPathName)(__in ULONG Flags, __inout PRTL_UNICODE_STRING_BUFFER Path, __out_opt PULONG Disposition, __inout_opt PWSTR* FilePart);

#define RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE (0x00000001)
#define RTL_DUPLICATE_UNICODE_STRING_ALLOCATE_NULL_STRING (0x00000002)
#define RTL_DUPSTR_ADD_NULL                          RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE
#define RTL_DUPSTR_ALLOC_NULL                        RTL_DUPLICATE_UNICODE_STRING_ALLOCATE_NULL_STRING

typedef NTSTATUS(WINAPI* pRtlDuplicateUnicodeString)(_In_ ULONG Flags, _In_ PUNICODE_STRING StringIn, _Out_ PUNICODE_STRING StringOut);

导入功能:

pRtlAnsiStringToUnicodeString MyRtlAnsiStringToUnicodeString;
pRtlNtPathNameToDosPathName MyRtlNtPathNameToDosPathName;
pRtlDuplicateUnicodeString MyRtlDuplicateUnicodeString;

MyRtlAnsiStringToUnicodeString = (pRtlAnsiStringToUnicodeString)GetProcAddress(GetModuleHandle("ntdll.dll"), "RtlAnsiStringToUnicodeString");
MyRtlNtPathNameToDosPathName = (pRtlNtPathNameToDosPathName)GetProcAddress(GetModuleHandle("ntdll.dll"), "RtlNtPathNameToDosPathName");
MyRtlDuplicateUnicodeString = (pRtlDuplicateUnicodeString)GetProcAddress(GetModuleHandle("ntdll.dll"), "RtlDuplicateUnicodeString");

辅助功能:

NTSTATUS NtPathNameToDosPathName(PUNICODE_STRING DosPath, PUNICODE_STRING NtPath)
{
    NTSTATUS                    Status;
    ULONG_PTR                   BufferSize;
    PWSTR                       Buffer;
    RTL_UNICODE_STRING_BUFFER   UnicodeBuffer;

    BufferSize = NtPath->MaximumLength + MAX_PATH * sizeof(WCHAR);

    Buffer = (PWSTR)_alloca(BufferSize);

    ZeroMemory(&UnicodeBuffer, sizeof(UnicodeBuffer));

    UnicodeBuffer.String = *NtPath;
    UnicodeBuffer.String.Buffer = Buffer;
    UnicodeBuffer.String.MaximumLength = (USHORT)BufferSize;
    UnicodeBuffer.ByteBuffer.Buffer = (PUCHAR)Buffer;
    UnicodeBuffer.ByteBuffer.Size = BufferSize;

    CopyMemory(Buffer, NtPath->Buffer, NtPath->Length);

    MyRtlNtPathNameToDosPathName(0, &UnicodeBuffer, NULL, NULL);

    return MyRtlDuplicateUnicodeString(RTL_DUPSTR_ADD_NULL, &UnicodeBuffer.String, DosPath);
}

函数用法:

UNICODE_STRING us;
UNICODE_STRING DosPath;
ANSI_STRING as;

as.Buffer = (char*)malloc(strlen(NT_PATH_FILE_OR_DIR) + 1);
strcpy(as.Buffer, NT_PATH_FILE_OR_DIR);
as.Length = as.MaximumLength = us.MaximumLength = us.Length = strlen(NT_PATH_FILE_OR_DIR);

MyRtlAnsiStringToUnicodeString(&us, &as, TRUE);

NtPathNameToDosPathName(&DosPath, &us);

如前所述,在我的情况下,我需要从 ANSI 转换为 UNICODE,这可能不适用于您的情况,因此您可以将其删除。

同上可用于创建自定义函数并根据需要转换路径。

于 2020-03-24T10:07:18.553 回答
2

检查这个以获取 Win32 中的规范路径名。它可能对您有帮助:

http://pdh11.blogspot.com/2009/05/pathcanonicalize-versus-what-it-says-on.html

于 2010-12-14T22:53:34.207 回答
2

请参阅我对这个问题的回答

您需要首先获取该路径中文件的句柄,然后获取该句柄的 Win32 路径。

于 2011-06-04T23:03:54.440 回答
0

我编写了一个函数,可以将不同类型的 NT 设备名称(文件名、COM 端口、网络路径等)转换为 DOS 路径。

有两个功能。一个将句柄转换为 NT 路径,另一个将 NT 路径转换为 ​​DOS 路径。

看看这里: 如何获取与打开 HANDLE 关联的名称

// "\Device\HarddiskVolume3"                                (Harddisk Drive)
// "\Device\HarddiskVolume3\Temp"                           (Harddisk Directory)
// "\Device\HarddiskVolume3\Temp\transparent.jpeg"          (Harddisk File)
// "\Device\Harddisk1\DP(1)0-0+6\foto.jpg"                  (USB stick)
// "\Device\TrueCryptVolumeP\Data\Passwords.txt"            (Truecrypt Volume)
// "\Device\Floppy0\Autoexec.bat"                           (Floppy disk)
// "\Device\CdRom1\VIDEO_TS\VTS_01_0.VOB"                   (DVD drive)
// "\Device\Serial1"                                        (real COM port)
// "\Device\USBSER000"                                      (virtual COM port)
// "\Device\Mup\ComputerName\C$\Boot.ini"                   (network drive share,  Windows 7)
// "\Device\LanmanRedirector\ComputerName\C$\Boot.ini"      (network drive share,  Windwos XP)
// "\Device\LanmanRedirector\ComputerName\Shares\Dance.m3u" (network folder share, Windwos XP)
// "\Device\Afd"                                            (internet socket)
// "\Device\Console000F"                                    (unique name for any Console handle)
// "\Device\NamedPipe\Pipename"                             (named pipe)
// "\BaseNamedObjects\Objectname"                           (named mutex, named event, named semaphore)
// "\REGISTRY\MACHINE\SOFTWARE\Classes\.txt"                (HKEY_CLASSES_ROOT\.txt)
于 2015-08-11T03:26:08.950 回答