1

我最近一直在以编程方式操纵重解析点,并且有一段时间一直困扰着我。由于 Windows 硬链接不像连接点或符号链接那样重新解析点,因此不能以相同的方式访问它们。创建一个新的很容易,但我还没有弄清楚如何读取一个目标。由于Hard Link Shell Extension之类的扩展具有显示该信息的属性表,因此我认为可以完成,但我一直无法找到任何有关如何操作的文档。(我确实注意到,shell 扩展名并不表示硬链接上哪个文件是真实的,不过)

我确实找到了这个答案,它解释了如何计算文件的链接,但不幸的是,我仍然坚持解决。

4

4 回答 4

2

$FILE_NAME硬链接信息使用 POSIX 名称存储在属性中。这些属性中的每一个都指代一个文件,该文件可能指代原始文件本身。没有任何硬链接的文件也可能有 POSIX 名称。换句话说,一个硬链接文件总是有多个 POSIX 名称。该属性的DirectoryFileReferenceNumber字段指向一个 MFT 条目索引,它是包含该文件的文件夹条目。

以下是检索文件的所有目标的指南,无论它是否是硬链接的。

您将需要FSCTL_GET_NTFS_FILE_RECORD在文件上使用以获取其所有 NTFS 属性并对其进行解析以检索每个$FILE_NAME属性。

在每个$FILE_NAME属性上,FSCTL_GET_NTFS_FILE_RECORD在文件所在的卷上使用其DirectoryFileReferenceNumber作为 MFT 条目索引来检索文件的文件夹容器信息。此文件夹是包含该文件的最低级别。例如,如果文件路径为C:\MyData\Myfiles\MyDocument.txt,则最低文件夹级别为MyFiles. 如果DirectoryFileReferenceNumber指向根文件夹,即0x5,则您拥有文件的完整路径。例如,C:\MyDocument.txt

使用该文件夹信息,如果它不是根文件夹,您只需重复与上述类似的任务,即从属性Name字段中检索文件夹名称。$FILE_NAME但是这一次,NameType不必是 POSIX 类型,任何都可以使用。最好是 LFN 或 LFN & DOS8.3 兼容类型。使用它DirectoryFileReferenceNumber来获取上层文件夹并重复本段中的任务。当DirectoryFileReferenceNumber指向根文件夹时,即0x5,则此重复任务已完成,因为您已经有了文件的完整路径。

现在您已经解决了一个文件目标。下一个任务是处理下一个POSIX 类型的$FILE_NAME属性。NameType处理所有这些以获取所有文件目标。不要使用这种方法来查明文件是否有硬链接。相反,使用GetFileAttributes函数,因为它要快得多。

于 2012-07-11T00:18:38.213 回答
0

我们在 Windows 上编写了一个专门用于连接点的 C++ 库,它在 MIT 许可下是开源的。您的问题是否不清楚这是否是您想要的,正如您所说的硬链接,但随后询问如何解决它们。

于 2012-04-21T20:58:23.710 回答
0

使用 GetFileInformationByHandle 检查文件是否有硬链接

typedef struct _BY_HANDLE_FILE_INFORMATION {
  DWORD    dwFileAttributes;
  FILETIME ftCreationTime;
  FILETIME ftLastAccessTime;
  FILETIME ftLastWriteTime;
  DWORD    dwVolumeSerialNumber;
  DWORD    nFileSizeHigh;
  DWORD    nFileSizeLow;
  DWORD    nNumberOfLinks;     <--- if this value is more than 1, then we have hard links
  DWORD    nFileIndexHigh;
  DWORD    nFileIndexLow;
} BY_HANDLE_FILE_INFORMATION, *PBY_HANDLE_FILE_INFORMATION, *LPBY_HANDLE_FILE_INFORMATION;

然后对于 vista 或更高版本,使用 FindFirstFileName/FindNextFileName 来获取目标。

对于较早的系统版本,遍历整个卷并比较nFileIndexHigh和nFileIndexLow,一旦找到,--nNumberOfLinks,打印目标名称,直到nNumberofLinks==1,然后退出。

这是我通过反转二进制文件在 FindLinks.exe 中找到的方式。

    if ( _wcsicmp(dword_422154, lpFileName) )
  {
    sub_402580(v4, (DWORD *)&v8, (int)&fileindexlow, (int)&v11);
    if ( fileindexlow == a3 && fileindexhigh == a4 )
    {
      wprintf(L"\r          \r%s\n", v4);
      if ( --*v5 == 1 )  //numoflinks
        exit(0);
    }
  }


char __usercall sub_402580@<al>(LPCWSTR lpFileName@<ecx>, DWORD *a2@<edx>, int a3, int a4)
{
  DWORD *v4; // edi
  DWORD v5; // eax
  HANDLE v6; // esi
  DWORD v7; // ecx
  const WCHAR *lpFileNamea; // [esp+Ch] [ebp-40h]
  struct _BY_HANDLE_FILE_INFORMATION FileInformation; // [esp+14h] [ebp-38h]

  v4 = a2;
  lpFileNamea = lpFileName;
  v5 = GetFileAttributesW(lpFileName);
  if ( v5 == -1 )
    return 0;
  *v4 = 0;
  v4[1] = 0;
  *(_DWORD *)a3 = 0;
  *(_DWORD *)(a3 + 4) = 0;
  *(_DWORD *)a4 = 0;
  v6 = CreateFileW(lpFileNamea, 0x80u, 7u, 0, 3u, (v5 & 0x10) << 21, 0);
  if ( v6 == (HANDLE)-1 )
    return 0;
  if ( GetFileInformationByHandle(v6, &FileInformation) )
  {
    *(_DWORD *)a4 = FileInformation.nNumberOfLinks;
    v7 = FileInformation.nFileSizeHigh;
    *v4 = FileInformation.nFileSizeLow;
    v4[1] = v7;
    LOWORD(v7) = FileInformation.nFileIndexHigh;
    *(_DWORD *)a3 = FileInformation.nFileIndexLow;
    *(_DWORD *)(a3 + 4) = (unsigned __int16)v7;
  }
  CloseHandle(v6);
  return 1;
}
于 2020-06-23T02:55:55.107 回答
0

以防万一其他人来寻找这些信息,我决定发布一个我在回答这个问题后写的小 POC。它基于 Jay 描述的步骤,并且应该与 Windows XP 和更高版本的操作系统兼容。

POC 代码

于 2020-12-13T00:17:46.853 回答