https://msdn.microsoft.com/en-us/library/windows/desktop/aa365521(v=vs.85).aspx

好久没写博客了,最近看了下DeviceIoControl  关于磁盘的应用,特记一文,以备久后查阅。

首先介绍下,文件在磁盘的存储结构(具体可以到网上查询NTFS文件系统相关的教程后者数据恢复方面教程的介绍)。下面介绍的仅与此文相关。

文件属性(头):

(Ps: 截图摘自[数据重现文件系统原理精解与数据恢复最佳实践].(马林))

然后我们需要认识两个结构:

[cpp] view plain copy
  1. typedef struct {
  2. LARGE_INTEGER StartingVcn;
  3. } STARTING_VCN_INPUT_BUFFER, *PSTARTING_VCN_INPUT_BUFFER;
[cpp] view plain copy
  1. typedef struct RETRIEVAL_POINTERS_BUFFER {
  2. DWORD         ExtentCount;
  3. LARGE_INTEGER StartingVcn;
  4. struct {
  5. LARGE_INTEGER NextVcn;
  6. LARGE_INTEGER Lcn;
  7. } Extents[1];
  8. } RETRIEVAL_POINTERS_BUFFER, *PRETRIEVAL_POINTERS_BUFFER;

通过使用参数

FSCTL_GET_RETRIEVAL_POINTERS  调用函数DeviceIoControl 我们就可以获得文件在磁盘中的定位信息。
方式如下:
[cpp] view plain copy
  1. DeviceIoControl(
  2. (HANDLE) hDevice,              // handle to volume
  3. FSCTL_GET_RETRIEVAL_POINTERS,  // dwIoControlCode
  4. (LPVOID) lpInBuffer,           // input buffer
  5. (DWORD) nInBufferSize,         // size of input buffer
  6. (LPVOID) lpOutBuffer,          // output buffer
  7. (DWORD) nOutBufferSize,        // size of output buffer
  8. (LPDWORD) lpBytesReturned,     // number of bytes returned
  9. (LPOVERLAPPED) lpOverlapped ); // OVERLAPPED structure
函数第三个参数对应上述第一个结构,此结构比较简单,需要传入文件的其实Vcn号,这里填入 0 即可 (StartingVcn.QuadPart = 0)。
第二个结构相对复杂些:
由上述介绍可以知道,文件(相对较大的文件)在磁盘中是以簇流(连续的簇)的形式存放的。结构体中 ExtentCount 即表示簇流的个数
StartingVcn 第一个簇流的起始Vcn号, 而每个Extents都包含一个NextVcn号和一个Lcn,Lcn即表示本簇流的起始Lcn,NextVcn是用来判断下一个簇流的位置(通过NextVcn也可以的到上一个簇流的大小)
下面是msdn的解释:
[html] view plain copy
  1. NextVcn
  2. The VCN at which the next extent begins. This value minus either StartingVcn (for the first Extents array member) or the NextVcn of the previous member of the array (for all other Extents array members) is the length, in clusters, of the current extent. The length is an input to the FSCTL_MOVE_FILE operation.
对于第一个簇流,NextVcn 减去 StartingVcn 即得到第一个簇流的大小,而对于后续的簇流,使用此NextVcn 减去上一个簇流的NextVcn即上一个簇流的大小。
所以根据此信息,我们能够得到文件在磁盘中簇流链的信息,从而定位文件,从磁盘中直接读取文件,具体代码如下(基本参考网上已有代码):
[cpp] view plain copy
  1. //
  2. /// ReadFileFromSectors.cpp
  3. #include <windows.h>
  4. #include <WinIoCtl.h>
  5. #include <stdio.h>
  6. ULONGLONG *GetFileClusters(PCHAR lpFilename, ULONG *ClusterSize, ULONG *ClusterCount, ULONG *FileSize)
  7. {
  8. HANDLE hFile = NULL;
  9. //磁盘基本信息变量定义
  10. ULONG SectorsPerCluster;
  11. ULONG BytesPerSector;
  12. STARTING_VCN_INPUT_BUFFER InVcvBuffer;   //输入的开始vcn号
  13. PRETRIEVAL_POINTERS_BUFFER pOutFileBuffer;  //输出的结果缓冲区
  14. ULONG OutFileSize;
  15. LARGE_INTEGER PreVcn,Lcn;
  16. ULONGLONG *Clusters = NULL;
  17. BOOLEAN bDeviceIoResult = FALSE;
  18. //逻辑路径(卷号)
  19. char DriverPath[8];
  20. memset(DriverPath, 0, sizeof(DriverPath));
  21. DriverPath[0] = lpFilename[0];
  22. DriverPath[1] = ':';
  23. DriverPath[2] = 0;
  24. GetDiskFreeSpace(DriverPath, &SectorsPerCluster, &BytesPerSector, NULL, NULL);
  25. *ClusterSize = SectorsPerCluster * BytesPerSector;
  26. //定位文件
  27. hFile = CreateFile(lpFilename,
  28. //GENERIC_READ | GENERIC_WRITE,
  29. FILE_READ_ATTRIBUTES,
  30. FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
  31. NULL,
  32. OPEN_EXISTING,
  33. 0,
  34. 0);
  35. if(hFile == INVALID_HANDLE_VALUE)
  36. {
  37. printf("GetFileClusters(): Failed to open file %s ...\n",lpFilename);
  38. return 0;
  39. }
  40. *FileSize = GetFileSize(hFile, NULL);
  41. //初始化IO相关参数
  42. DWORD dwRead, Cls, CnCount, r;
  43. OutFileSize = sizeof(RETRIEVAL_POINTERS_BUFFER) + (*FileSize / *ClusterSize) * sizeof(pOutFileBuffer->Extents);  //个人认为这个结果应该比实际所需的缓冲区大
  44. pOutFileBuffer = (PRETRIEVAL_POINTERS_BUFFER)malloc(OutFileSize);
  45. InVcvBuffer.StartingVcn.QuadPart = 0;
  46. //调用函数后去信息
  47. bDeviceIoResult = DeviceIoControl(hFile,
  48. FSCTL_GET_RETRIEVAL_POINTERS,
  49. &InVcvBuffer,
  50. sizeof(InVcvBuffer),
  51. pOutFileBuffer,
  52. OutFileSize,
  53. &dwRead,
  54. NULL);
  55. if(!bDeviceIoResult)
  56. {
  57. printf("GetFileClusters(): Failed to call DeviceIocontrol with paramter FSCTL_GET_RETRIEVAL_POINTERS...\n|---errorcode = %d\n",GetLastError());
  58. CloseHandle(hFile);
  59. return 0;
  60. }
  61. *ClusterCount = (*FileSize + *ClusterSize -1) / *ClusterSize;   //Cluster 数组的大小,一个簇占一个元素
  62. Clusters = (ULONGLONG *)malloc(*ClusterCount * sizeof(ULONGLONG));   //分配簇数组空间
  63. //开始遍历返回结果
  64. PreVcn = pOutFileBuffer->StartingVcn;
  65. for(r=0,Cls=0; r<pOutFileBuffer->ExtentCount; r++)   //ExtentCount 簇流的个数(每个簇流中有几个连续的簇)
  66. {
  67. Lcn = pOutFileBuffer->Extents[r].Lcn;
  68. //簇流中连续簇的个数等于 下一个簇流的起始 Vcn 号 减去 上一个 簇流的 起始 Vcn号
  69. for(CnCount = (ULONG)(pOutFileBuffer->Extents[r].NextVcn.QuadPart - PreVcn.QuadPart); CnCount; CnCount--,Cls++,Lcn.QuadPart++)
  70. {
  71. Clusters[Cls] = Lcn.QuadPart;  //保存每个簇流中簇的 Lcn 号
  72. }
  73. PreVcn = pOutFileBuffer->Extents[r].NextVcn;
  74. }
  75. free(pOutFileBuffer);
  76. CloseHandle(hFile);
  77. return Clusters;
  78. }
  79. int ReadFileFromSectors(PCHAR lpFileName, PCHAR pDstFileName)
  80. {
  81. ULONG ClusterSize, BlockSize, ClusterCount, FileSize;
  82. ULONGLONG *Clusters = NULL;
  83. DWORD dwReads,dwWrites;
  84. HANDLE hDriver, hFile;
  85. ULONG SectorsPerCluster, BytesPerSector, r;
  86. PVOID FileBuff;  //存放从扇区中读取的数据
  87. LARGE_INTEGER offset;
  88. char DrivePath[10];
  89. Clusters = GetFileClusters(lpFileName, &ClusterSize, &ClusterCount, &FileSize);
  90. if(Clusters == NULL)
  91. {
  92. printf("ReadFileFromSectors(): Failed to GetFileClusters ...\n|---errrorcode = %d\n",GetLastError());
  93. return 0;
  94. }
  95. DrivePath[0] = '\\';
  96. DrivePath[1] = '\\';
  97. DrivePath[2] = '.';
  98. DrivePath[3] = '\\';
  99. DrivePath[4] = lpFileName[0];
  100. DrivePath[5] = ':';
  101. DrivePath[6] = 0;
  102. //打开磁盘卷
  103. hDriver = CreateFile(DrivePath,
  104. GENERIC_READ,
  105. FILE_SHARE_READ | FILE_SHARE_WRITE,
  106. NULL,
  107. OPEN_EXISTING,
  108. 0,
  109. NULL);
  110. if(hDriver == INVALID_HANDLE_VALUE)
  111. {
  112. printf("ReadFileFromSectors(): Failed to CreateFile %s ...\n|---errrorcode = %d\n",DrivePath,GetLastError());
  113. return 0;
  114. }
  115. //存放读出的文件
  116. hFile = CreateFile(pDstFileName, GENERIC_WRITE, 0, NULL, CREATE_NEW, 0, 0);
  117. if(hFile == INVALID_HANDLE_VALUE)
  118. {
  119. printf("ReadFileFromSectors(): Failed to CreateFile %s ...\n|---errrorcode = %d\n",pDstFileName,GetLastError());
  120. return 0;
  121. }
  122. FileBuff = malloc(ClusterSize);
  123. //开始读扇区文件内容
  124. for (r=0; r<ClusterCount; r++, FileSize -= BlockSize)
  125. {
  126. offset.QuadPart = ClusterSize * Clusters[r];    //确定每个簇的偏移
  127. SetFilePointer(hDriver, offset.LowPart, &offset.HighPart, FILE_BEGIN);
  128. ReadFile(hDriver, FileBuff, ClusterSize, &dwReads, NULL);  //每次读一个簇的大小
  129. BlockSize = FileSize < ClusterSize ? FileSize : ClusterSize;
  130. WriteFile(hFile, FileBuff, BlockSize, &dwWrites, NULL);  //将读取的文件保存起来
  131. }
  132. free(FileBuff);
  133. free(Clusters);
  134. CloseHandle(hFile);
  135. CloseHandle(hDriver);
  136. }
  137. //--------------------------------------------------------------------
  138. //
  139. // Usage
  140. //
  141. // Tell user how to use the program.
  142. //
  143. //--------------------------------------------------------------------
  144. int Usage( CHAR *ProgramName )
  145. {
  146. printf("\nusage: %s -f srcfile dstfile ...\n", ProgramName );
  147. return -1;
  148. }
  149. int main(int argc, char *argv[])
  150. {
  151. if(argc != 4)
  152. {
  153. Usage(argv[0]);
  154. return 0;
  155. }
  156. //读文件
  157. if(strcmp(argv[1], "-f") == 0)
  158. {
  159. ReadFileFromSectors(argv[2], argv[3]);
  160. }
  161. else
  162. {
  163. Usage(argv[0]);
  164. }
  165. system("pause");
  166. return 1;
  167. }

编译程序,以管理员权限运行,测试结果如下:


Ps: 此方法还有些弊端,文件不能使加密、压缩的文件,而且文件必须是非常驻的(相对大些的文件即要有自己的簇),对于常驻的(小文件),文件内容直接存放到文件的MFT中,此方法是读不到的。


取得文件所在磁盘的扇区大小

  1. int GetSectorSize(LPCWSTR Path) {
  2. wchar_t buf[MAX_PATH + 1] = { L"" };
  3. DWORD SecPerClu, BytePerSec, NumFreeClu, TotalClu;
  4. UINT  DriveType;
  5. if (GetFullPathName(Path, MAX_PATH, buf, NULL) == 0) { return 0; }
  6. if (!buf[0])                                         { return 0; }
  7. buf[3] = 0x00;
  8. DriveType = GetDriveType(buf);
  9. if (DriveType != DRIVE_FIXED && DriveType != DRIVE_REMOVABLE) { return 0; }
  10. if (GetDiskFreeSpace(buf, &SecPerClu, &BytePerSec, &NumFreeClu, &TotalClu)) {
  11. return ((int)BytePerSec);
  12. }
  13. return 0;
  14. }

直接读取扇区来拷贝文件的例子

使用函数DeviceIoControl(hFile, FSCTL_GET_RETRIEVAL_POINTERS, &InBuf,    sizeof(InBuf), OutBuf, OutSize, &Bytes, NULL))就可以查询文件的簇链

下面是个例如直接读取扇区来拷贝文件的例子用法:FileCopy("C://boot.ini", "D://boot.ini");
C/C++ code
ULONGLONG *GetFileClusters(LPCSTR lpFileName,ULONG ClusterSize, ULONG *ClCount,ULONG *FileSize ) {HANDLE hFile;ULONG OutSize;ULONG Bytes, Cls, CnCount, r;ULONGLONG *Clusters = NULL;BOOLEAN Result = FALSE;LARGE_INTEGER PrevVCN, Lcn;STARTING_VCN_INPUT_BUFFER InBuf;PRETRIEVAL_POINTERS_BUFFER OutBuf;hFile = CreateFile(lpFileName, FILE_READ_ATTRIBUTES,FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,NULL, OPEN_EXISTING, 0, 0);if (hFile != INVALID_HANDLE_VALUE){*FileSize = GetFileSize(hFile, NULL);OutSize = sizeof(RETRIEVAL_POINTERS_BUFFER) + (*FileSize / ClusterSize) * sizeof(OutBuf->Extents);OutBuf = (PRETRIEVAL_POINTERS_BUFFER)malloc(OutSize);InBuf.StartingVcn.QuadPart = 0;if (DeviceIoControl(hFile, FSCTL_GET_RETRIEVAL_POINTERS, &InBuf, sizeof(InBuf), OutBuf, OutSize, &Bytes, NULL)){*ClCount = (*FileSize + ClusterSize - 1) / ClusterSize;Clusters = (PULONGLONG)malloc(*ClCount * sizeof(ULONGLONG));PrevVCN = OutBuf->StartingVcn;for (r = 0, Cls = 0; r < OutBuf->ExtentCount; r++){Lcn = OutBuf->Extents[r].Lcn;for (CnCount = (ULONG)(OutBuf->Extents[r].NextVcn.QuadPart - PrevVCN.QuadPart);CnCount; CnCount--, Cls++, Lcn.QuadPart++) Clusters[Cls] = Lcn.QuadPart;PrevVCN = OutBuf->Extents[r].NextVcn;}}free(OutBuf); CloseHandle(hFile);}return Clusters; }BOOL FileCopy(LPCSTR lpSrcName, LPCSTR lpDstName) {BOOL bResult = FALSE;ULONG ClusterSize, BlockSize;ULONGLONG *Clusters;ULONG ClCount, FileSize, Bytes;HANDLE hDrive, hFile;ULONG SecPerCl, BtPerSec, r;PVOID Buff;LARGE_INTEGER Offset;CHAR Name[7];Name[0] = lpSrcName[0];Name[1] = ':';Name[2] = 0;GetDiskFreeSpace(Name, &SecPerCl, &BtPerSec, NULL, NULL);ClusterSize = SecPerCl * BtPerSec;Clusters = GetFileClusters(lpSrcName, ClusterSize, &ClCount, &FileSize);if(Clusters){Name[0] = '//';Name[1] = '//';Name[2] = '.';Name[3] = '//';Name[4] = lpSrcName[0];Name[5] = ':';Name[6] = 0;hDrive = CreateFile(Name, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0);if (hDrive != INVALID_HANDLE_VALUE){hFile = CreateFile(lpDstName, GENERIC_WRITE, 0, NULL, CREATE_NEW, 0, 0);if (hFile != INVALID_HANDLE_VALUE){Buff = malloc(ClusterSize);for (r = 0; r < ClCount; r++, FileSize -= BlockSize){Offset.QuadPart = ClusterSize * Clusters[r];SetFilePointer(hDrive, Offset.LowPart, &Offset.HighPart, FILE_BEGIN);ReadFile(hDrive, Buff, ClusterSize, &Bytes, NULL);BlockSize = FileSize < ClusterSize ? FileSize : ClusterSize;WriteFile(hFile, Buff, BlockSize, &Bytes, NULL);}free(Buff);CloseHandle(hFile);bResult = TRUE;}CloseHandle(hDrive);}free(Clusters);}else{printf("GetFileClusters fail./n");}return bResult; }

DeviceIoControl 直接从磁盘扇区读文件相关推荐

  1. 如何删除“无法读源文件或磁盘”的空头文件

     如何删除"无法读源文件或磁盘"的空头文件 WinNT下不能建立全部是空格的文件,但是一般我们下载的时候可能不小心就下载了这类全部是空格的文件,不管怎么删除比如说DOS下:杀死Ex ...

  2. windows下修改磁盘扇区数据

    Windows系统提供了文件系统,通常应用程序读写文件都是调用CreateFile函数来实现,不会直接读写磁盘数据,但某些特殊目的中,我们需要读取磁盘扇区数据,查看是否包含某些关键字,并且有可能擦除这 ...

  3. c++ 读文件 文件指针 继续读_FatFs文件系统使用笔记

    关于FatFs文件系统的指引 关于FATFS的使用记录一.提前说明二.各文件说明三.使用说明       3.1 diskio.c函数的编写       3.2 f_open读写性质说明        ...

  4. python读写磁盘扇区数据_[Win32] 直接读写磁盘扇区(磁盘绝对读写)

    正讲着驱动开发呢,这里突然插一篇Win32的博文.事实上.还是做引子,上一篇博文"IRP与派遣函数"中,我们知道了驱动程序须要处理I/O请求.我们先来看看怎么发出一个I/O请求. ...

  5. 【CentOS Linux 7】实验3【用户组群管理、磁盘分区及文件系统管理】

    Linux系统及应用---调研报告 [CentOS Linux 7]实验1[VMware安装.新建虚拟机:63个基础命令运行结果图] [CentOS Linux 7]实验2[Shell编程及应用] [ ...

  6. 从磁盘原理理解文件读写优化

    目录 案例背景 磁盘结构 盘面 磁道 柱面 启停区或着陆区(LandingZone) 扇区 读写过程 磁盘碎片的产生(1) 希捷硬盘读写性能测试报告 文件碎片 文件碎片定义 产生 原因 文件碎片和连续 ...

  7. [Win32] 直接读写磁盘扇区(磁盘绝对读写)

    本博文由CSDN博主zuishikonghuan所作,版权归zuishikonghuan所有,转载请注明出处:http://blog.csdn.net/zuishikonghuan/article ...

  8. python读写磁盘扇区数据_linux 读写磁盘扇区

    广告关闭 云服务器1核2G首年99年,还有多款热门云产品满足您的上云需求 为何更改为 4096 字节扇区? 如果您熟悉磁盘结构,就知道磁盘是被分解成扇区 的,大小通常是 512 字节: 所有读写操作均 ...

  9. 磁盘管理及文件系统管理

    Linux磁盘管理及文件系统管理 涉及到的命令: mknod.fdisk.partx.kpartx.partprobe.ln.mkfs.mkfs.{ext2.ext3.ext4.xfs}.fsck.{ ...

最新文章

  1. 计算机视觉相关干货文章-20190807
  2. 自学php心得体会,php5 学习心得
  3. C#模板设计模式使用和学习心得
  4. WinCE/Mobile上下滑动浏览DataGrid数据 【转】
  5. 三轴加速度传感器和六轴惯性传感器_[算法][三轴、六轴、九轴传感器算法分析] 1、分享一个三轴加速计matlab动态可视化脚本...
  6. 信用卡到底逾期多少天才会上征信系统?
  7. nginx停止,平滑重启命令
  8. 【BZOJ2908】又是nand 树链剖分+线段树
  9. AE学习笔记——第二章:AE图层中的图层用法及基本操作
  10. 记一次node+react项目发布过程(一)--webpack生产环境打包优化
  11. ViBe(Visual Background extractor)背景建模或前景检测
  12. 南大cssci期刊目录_最新版CSSCI来源期刊目录(2019-2020)及增减变化!【南大核心】...
  13. 视觉(5)A Fast Area-Based Stereo Matching Algorithm
  14. Android百度SDK定位
  15. csv数据导入mysql方法
  16. 添加地图图例 Arcengine+C#
  17. netbackup备份mysql_NBU网络备份大全之数据库备份与恢复
  18. 一些优秀的源码开源网站
  19. 组网胖模式_华三无线apEWP-WA4320i-acn-fit 如何由瘦模式改为胖模式
  20. CentOS7安装mysql5.7.19的手顺,步骤(非常详细)

热门文章

  1. 数字图像处理之图像的梯度倒数加权平滑:用Java+OpenCV实现,附源码
  2. [OpenGL]配置GLFW(超详细)
  3. 【ArcGIS二次开发】Engine界面搭建
  4. SSL 根证书的相关概念
  5. ArcGIS二次开发——面要素等面积分割
  6. Apache下载安装
  7. C# 使用BitBlt进行窗口抓图的示例
  8. Android移动应用开发教程④
  9. linux命令stat,Linux stat命令参数及使用方法详解
  10. html判断是否为文件夹,FSO(fileSystemObject)判断文件及文件夹是否存在