【问题标题】:Get the immediate target path from symlink/reparse point从符号链接/重解析点获取直接目标路径
【发布时间】:2017-09-23 19:21:20
【问题描述】:

我知道 GetFinalPathNameByHandle 可用于获取符号链接或重解析点的目标,但在某些情况下不宜使用它:

  • 如果目标不可用、不存在或无法打开,则符号链接上的CreateFile 失败,因此无法获取路径。
  • 如果我将符号链接“a”指向文件“b”并创建符号链接“b”到文件“c”,则函数将遵循整个链,返回“c”。
  • 当我已经掌握了手头实际符号链接的句柄时,该函​​数没有多大用处。

似乎 DeviceIoControl 可以与FSCTL_GET_REPARSE_POINT 一起使用以获取文件的实际重解析数据,但这让我得到REPARSE_DATA_BUFFER,我将不得不解析那个。

我不知道系统实际上是如何处理重解析点的,但我认为目标位置是一条应该在某个时候可用的信息。例如,dir 命令可以正确显示任何重解析点的目标路径……好吧,我已经看到它只处理符号链接和挂载点(连接点)。

【问题讨论】:

  • 经过一些逆向工程后,似乎 cmd.exe 确实调用了 DeviceIoControl 并解析了数据,但仅限于符号链接或联结。
  • 您需要使用FILE_FLAG_OPEN_REPARSE_POINT 选项打开文件。只有在这种情况下,您才能发送FSCTL_GET_REPARSE_POINT。否则将是ERROR_NOT_A_REPARSE_POINT
  • @RbMm 是的,我知道。忘了说。

标签: windows winapi symlink


【解决方案1】:

系统如何实际处理重解析点

这是在文件系统和文件系统过滤器驱动程序中完成的。结果取决于调用CreateFile(或NT调用中的FILE_OPEN_REPARSE_POINT)中使用的FILE_FLAG_OPEN_REPARSE_POINT选项。

当指定FILE_FLAG_OPEN_REPARSE_POINT 时 - 文件系统绕过对文件的正常重解析点处理,并尝试按原样直接打开重解析点文件。

如果未指定 FILE_OPEN_REPARSE_POINT 标志 - 文件系统尝试打开重解析点指向的文件(如果 fs 理解重解析点的格式 - 仅主要 Microsoft 重解析点)

重解析点保存的数据格式为REPARSE_DATA_BUFFER(微软重解析点格式)或REPARSE_GUID_DATA_BUFFER - 需要在开始时查找ReparseTag

我们使用IsReparseTagMicrosoft 宏来确定重解析点标签是否对应于微软拥有的标签。

测试/打印重解析点数据的代码:

volatile UCHAR guz;

ULONG TestReparsePoint(PCWSTR FileName)
{
    HANDLE hFile = CreateFile(FileName, 0, FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING, 
        FILE_FLAG_OPEN_REPARSE_POINT|FILE_FLAG_BACKUP_SEMANTICS, 0);

    if (hFile == INVALID_HANDLE_VALUE) 
    {
        return GetLastError();
    }

    union {
        PVOID pv;
        PULONG ReparseTag;
        PREPARSE_DATA_BUFFER prdb;
        PREPARSE_GUID_DATA_BUFFER prgdb;
    };

    PVOID stack = alloca(guz);

    ULONG cb = 0, rcb = sizeof(REPARSE_DATA_BUFFER) + 0x100, BytesReturned;

    ULONG dwError;

    do 
    {
        if (cb < rcb) cb = RtlPointerToOffset(pv = alloca(rcb - cb), stack);

        if (DeviceIoControl(hFile, FSCTL_GET_REPARSE_POINT, 0, 0, pv, cb, &BytesReturned, 0))
        {
            dwError = NOERROR;

            if (IsReparseTagMicrosoft(*ReparseTag))
            {
                char cc[16];
                LPCSTR name;

                switch (*ReparseTag)
                {
                case IO_REPARSE_TAG_SYMLINK:
                    name = " SYMLINK";
                    stack = prdb->SymbolicLinkReparseBuffer.PathBuffer;
                    break;
                case IO_REPARSE_TAG_MOUNT_POINT:
                    name = " MOUNT_POINT";
                    stack = prdb->MountPointReparseBuffer.PathBuffer;
                    break;
                default:
                    sprintf(cc, " %08x", prdb->ReparseTag);
                    name = cc;
                }

                DbgPrint(" %s->%.*S <%.*S>\n", name, 
                    prdb->MountPointReparseBuffer.SubstituteNameLength >> 1,
                    RtlOffsetToPointer(stack, prdb->MountPointReparseBuffer.SubstituteNameOffset),
                    prdb->MountPointReparseBuffer.PrintNameLength >> 1,
                    RtlOffsetToPointer(stack, prdb->MountPointReparseBuffer.PrintNameOffset)
                    );
            }
            else
            {
                PGUID g = &prgdb->ReparseGuid;
                DbgPrint(" tag=%x {%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x} size=%x\n", *ReparseTag, 
                    g->Data1, g->Data2, g->Data3, 
                    g->Data4[0],g->Data4[1],g->Data4[2],g->Data4[3],g->Data4[4],g->Data4[5],g->Data4[6],g->Data4[7],
                    prgdb->ReparseDataLength);

            }
            break;
        }

        rcb = IsReparseTagMicrosoft(*ReparseTag) 
            ? REPARSE_DATA_BUFFER_HEADER_SIZE + prdb->ReparseDataLength 
            : REPARSE_GUID_DATA_BUFFER_HEADER_SIZE + prgdb->ReparseDataLength;

    } while((dwError = GetLastError()) == ERROR_MORE_DATA);

    CloseHandle(hFile);

    return dwError;
}

【讨论】:

  • @JonathanPotter - 你能指出 UB 和内存泄漏吗?我坚持没有任何这些
【解决方案2】:

Microsoft 重解析点可以改为使用REPARSE_DATA_BUFFER 读取。 MS open protocol specification 也可能有用。

只有知道格式才能解析其他基于 GUID 的标签。

【讨论】:

  • 我刚刚编辑了这个问题以澄清我的意思是REPARSE_DATA_BUFFER,这是用于符号链接和连接的那个。
猜你喜欢
  • 2023-03-17
  • 2011-01-19
  • 2012-10-28
  • 2013-07-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-02-12
相关资源
最近更新 更多