以编程方式查找Windows Hard链接的目标

时间:2012-04-21 16:06:15

标签: winapi ntfs hardlink

最近我以编程方式操作重新分析点一直在玩很多东西,现在有些东西一直困扰着我。由于Windows硬链接不是像路口或符号链接那样的重新分析点,因此无法以相同的方式访问它们。创建一个新的很容易,但我还没弄明白如何阅读一个目标。由于像Hard Link Shell Extension这样的扩展程序具有显示该信息的属性表,我认为可以完成,但我一直无法找到有关如何使用的任何文档。 (我确实注意到shell扩展并没有指出哪个文件是硬链接上真实的东西)

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

4 个答案:

答案 0 :(得分:2)

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

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

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

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

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

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

答案 1 :(得分:0)

我们写了a C++ library致力于Windows上的连接点,它是MIT许可下的开源。从你的问题中可以看出这是否是你想要的,正如你所说的硬链接,但随后询问如何解决它们。

答案 2 :(得分: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;
}

答案 3 :(得分:0)

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

POC Code