通过DeviceIoControl读磁盘的方式读取独占文件内容
前言
windows操作系统中常见的一个文件存储系统是NTFS。在这个文件系统中MFT是它的核心。

图一
MFT是一个数据结构,上图是它的结构,它主要用来存放每个文件和目录在磁盘中的索引。MFT由一个个MFT项构成。每个MFT项的大小是1024Bytes。每个MFT项由固定头结构和多个属性构成。属性用来记录与文件相关的信息。每个属性又由属性头和属性体构成。属性头包含了一些该属性的重要信息,如属性类型、属性大小、是否为常驻属性等等。[1]文件内容也被NTFS看作属性。
其中,属性分为常驻属性和非常驻属性。如果一个属性的属性体都可以存储在MFT项中,称该属性是常驻属性。如果1024Bytes的MFT项无法记录该属性的所有属性体内容,称该属性是非常驻属性。
非常驻属性的属性体里存储的不是文件真正的内容,而是文件内容的起始簇号和簇号个数(文件内容存放在MFT外部)。前者方便找到文件在磁盘的位置,后者方便确定文件的大小。这两个信息称为簇流信息。有时候一个文件的内容被存储在多个簇流中,系统就用多个簇流信息来表示文件各部分内容的位置及大小。其中,除了文件的第一个簇流信息的起始簇号代表真正簇号,后面的每个簇流信息中的起始簇号记录的值是前一个起始簇号的相对位置。[2]
如果我们需要读磁盘上的某个文件,就可以这样操作——从MFT项里获取文件各部分内容的簇流信息,从中获得每个簇流的起始簇号,再用读磁盘的方式打开一个读磁盘句柄,将文件指针依次定位到各簇流的起始簇号位置,依次读出每个簇流内容,最后汇总起来就是该文件的所有内容啦。带着这个方案,使用DeviceIOControl()函数,我们就可以获得MFT项里的簇流信息。这个函数重点需要两个结构体,它们定义在<WinIoCtl.h>头文件里:
typedef struct {
LARGE_INTEGER StartingVcn;
} STARTING_VCN_INPUT_BUFFER, *PSTARTING_VCN_INPUT_BUFFER;
typedef struct RETRIEVAL_POINTERS_BUFFER {
DWORD ExtentCount;
LARGE_INTEGER StartingVcn;
struct {
LARGE_INTEGER NextVcn;
LARGE_INTEGER Lcn;
} Extents[];
} RETRIEVAL_POINTERS_BUFFER, *PRETRIEVAL_POINTERS_BUFFER;
其中,LCN是逻辑簇号,即这个簇在磁盘中的序号;VCN是虚拟簇号,即这个簇在所属文件所有簇中的序号。第一个结构体中的StartingVcn是文件的起始VCN号,一般值为0。第二个结构体中的ExtentCount表示簇流的数量;StartingVcn表示第一个簇流的起始VCN簇号,一般为0;Extent结构体存放两个与簇流相关的信息,其中NextVcn表示下一个簇流的起始Vcn簇号;Lcn表示该簇流的逻辑簇号。有多少簇流我们就给第二个结构体分配多少个Extent结构体,虽然第二个结构体在定义时只有一个Extent结构体,但是未来给第二个结构体分配堆空间时,可以按照簇流数量来分配Extent结构体。

图二
假设一个文件有三个簇流,则如上图所示。某个簇流的NextVcn减去上一个簇流的NextVcn就可以得到该簇流有多少个簇,每个簇大小可以用GetDiskFreeSpace()函数计算出来,并且一个簇流中的簇的Lcn是顺序累加的。这样我们从该簇流的起始Lcn开始,循环累加Lcn,每次读出簇大小的内容,直到读完该簇流。一个簇流的内容就获得了。然后根据下一个簇流的Lcn,执行相同的操作。直到把整个文件内容读完。
代码
typedef unsigned long ULONG;
typedef unsigned long long ULONGLONG;
void ReadDisk(char *pFilePath, char *pDrive, char *pDriveFile)
{
//--start -get bytes in one cluster [gbioc]
ULONG sectorsPerCluster, bytesPerSectors, ClusterSize;
GetDiskFreeSpace(pDrive, §orsPerCluster, &bytesPerSectors, NULL, NULL);
ClusterSize = sectorsPerCluster * bytesPerSectors;
//--end [gbioc] HANDLE hFile = CreateFile(pFilePath, FILE_READ_ATTRIBUTES, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, , NULL); //--start -get file size [gfz]
ULONGLONG fileSize;
BY_HANDLE_FILE_INFORMATION fileInfo;
GetFileInformationByHandle(hFile, &fileInfo);
fileSize = fileInfo.nFileSizeLow + ((ULONGLONG)fileInfo.nFileSizeHigh << );
//--end [gfz] ULONG clusterCount = (fileSize + ClusterSize - ) / ClusterSize; //--start -get file's cluster info in disk [gfciid]
STARTING_VCN_INPUT_BUFFER vcnBuf;
PRETRIEVAL_POINTERS_BUFFER pOutBuf;
ULONG outBufSize, readBytes;
outBufSize = (ULONG)&(((PRETRIEVAL_POINTERS_BUFFER))->Extents) + clusterCount * sizeof(pOutBuf -> Extents); //consider that each cluster is in different extent
pOutBuf = (PRETRIEVAL_POINTERS_BUFFER)malloc(outBufSize * sizeof(char));
memset(pOutBuf, 0x0, ); vcnBuf.StartingVcn.QuadPart = ;
DeviceIoControl(hFile, FSCTL_GET_RETRIEVAL_POINTERS, &vcnBuf, sizeof(vcnBuf), pOutBuf, outBufSize*sizeof(char), &readBytes, NULL); //①.
//--end [gfciid] ULONG extentCount = pOutBuf -> ExtentCount; //--start -put each cluster of file into array [pecofia]
ULONG *Clusters = (ULONG *)malloc(sizeof(ULONG) * clusterCount);
LARGE_INTEGER curLcn;
LARGE_INTEGER preVcn = pOutBuf -> StartingVcn;
ULONG extentIndx, clusterIndx, clustersInOneExtent;
for (extentIndx=,clusterIndx=; extentIndx<extentCount; ++extentIndx,++clusterIndx){
curLcn = pOutBuf -> Extents[extentIndx].Lcn;
for (clustersInOneExtent = (ULONG)(pOutBuf -> Extents[extentIndx].NextVcn.QuadPart - preVcn.QuadPart); clustersInOneExtent; ++clusterIndx, --clustersInOneExtent, ++curLcn.QuadPart){
Clusters[clusterIndx] = curLcn.QuadPart;
}
preVcn = pOutBuf -> Extents[extentIndx].NextVcn;
} free(pOutBuf);
CloseHandle(hFile);
//--end [pecofia] //--start -read drive like file
HANDLE hDrive;
hDrive = CreateFile(pDriveFile, GENERIC_READ, FILE_SHARE_WRITE | FILE_SHARE_READ, NULL, OPEN_EXISTING, , NULL);
LARGE_INTEGER offset;
char *pBuf = (char *)malloc(sizeof(char) * ClusterSize);
for (clusterIndx=; clusterIndx<clusterCount; ++clusterIndx){
offset.QuadPart = ClusterSize * Clusters[extentIndx];
SetFilePointer(hDrive, offset.LowPart, &offset.HighPart, FILE_BEGIN);
ReadFile(hDrive, pBuf, ClusterSize * sizeof(char), &readBytes, NULL);
} free(pBuf);
free(Clusters);
CloseHandle(hDrive);
//--end
} int main()
{
ReadDisk("C:\\file.dat", "C:", "\\\\.\\C:"); return ;
}
以上代码因为专注于功能所以没有加入错误控制,但是实际应用中我们应该对每个Win API、堆分配进行错误控制。
①:关于‘FSCTL_GET_RETRIEVAL_POINTERS’标志位,以下是MSDN上的解释:
The FSCTL_GET_RETRIEVAL_POINTERS operation retrieves a variably sized data structure that describes the allocation and location on disk of a specific file. The structure describes the mapping between virtual cluster numbers (VCN offsets within the file or stream space) and logical cluster numbers (LCN offsets within the volume space).
使用这个标志位可以获得指定文件的内容信息,即使待读文件是独占文件,依然可以用这个标识位获取文件的簇流信息,然后通过读磁盘的方式读出文件内容。
注:如果独占文件,DeviceIoControl的这个标志位使用GENEIC_READ的话,会返回错误句柄。
结尾
欢迎在评论区留言,说出你的见解,大家一起讨论~
参考
[2]. http://blog.51cto.com/shujvhuifu/1813848
通过DeviceIoControl读磁盘的方式读取独占文件内容的更多相关文章
- java 实现poi方式读取word文件内容
1.下载poi的jar包 下载地址:https://www.apache.org/dyn/closer.lua/poi/release/bin/poi-bin-3.17-20170915.tar.gz ...
- 五种方式让你在java中读取properties文件内容不再是难题
一.背景 最近,在项目开发的过程中,遇到需要在properties文件中定义一些自定义的变量,以供java程序动态的读取,修改变量,不再需要修改代码的问题.就借此机会把Spring+SpringMVC ...
- C++->以读或写方式打开一个文件
以读或写方式打开一个文件 #include<iostream.h> //.h以C|非C标准引用库文件 #include<fstream.h> #include<std ...
- 手工创建tomcat应用,以及实现js读取本地文件内容
手工创建tomcat应用: 1.在webapps下面新建应用目录文件夹 2.在文件夹下创建或是从其他应用中复制:META-INF,WEB-INF这两个文件夹, 其中META-INF清空里面,WEB-I ...
- java读取txt文件内容
package read; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; public ...
- 读取Pdm文件内容(含源码)
Pdm文件,就是PowerDesigner软件生成的文件,用来设计数据库表结构非常适合.其实,它的文件存储格式就是Xml,网上有很多代码,可以读取pdm文件内容.代码可以使用,但一般只能读取简单的pd ...
- 深入学习python解析并读取PDF文件内容的方法
这篇文章主要学习了python解析并读取PDF文件内容的方法,包括对学习库的应用,python2.7和python3.6中python解析PDF文件内容库的更新,包括对pdfminer库的详细解释和应 ...
- PHP读取Excel文件内容
PHP读取Excel文件内容 项目需要读取Excel的内容,从百度搜索了下,主要有两个选择,第一个是PHPExcelReader,另外一个是PHPExcel. PHPExcelReader比较 ...
- 使用POI读取excel文件内容
1.前言 项目中要求读取excel文件内容,并将其转化为xml格式.常见读取excel文档一般使用POI和JExcelAPI这两个工具.这里我们介绍使用POI实现读取excel文档. 2.代码实例: ...
随机推荐
- python接口自动化-json数据处理
前言 有些post的请求参数是json格式的,需要导入json模块进行处理,json是一种数据交换格式,独立于编程语言 一般常见的接口返回数据也是json格式的,我们在做判断的时候,往往只需要提取其中 ...
- c++11の泛型算法
一.泛型算法泛型算法这个概念是针对容器操作的,我们知道,c++11的顺序容器有vector,list,deque等,对于这些容器,c++11并没给出相应的增删改查方法,而是定义了一组泛型算法 一般的泛 ...
- [已解决]python FileNotFoundError: [WinError 3] for getsize(filepath)
问题代码: def sourceStatic(path, exclude): # exclude list convert to lower exclude = list(map(lambda x:x ...
- git 多用户多仓库配置
ssh全称是Secure Shell,即安全Shell,是一种可以进行安全远程登录的协议,在Linux中以OpenSSH为代表,Windows中则有Putty作为实现.ssh的会话建立阶段类似TCP协 ...
- Linux内存管理专题
Linux的内存管理涉及到的内容非常庞杂,而且与内核的方方面面耦合在一起,想要理解透彻非常困难. 在开始学习之前进行了一些准备工作<如何展开Linux Memory Management学习?& ...
- 一键发布部署vs插件[AntDeploy],让net开发者更幸福
一键发布工具(ant deploy tool) 插件下载地址: https://marketplace.visualstudio.com/items?itemName=nainaigu.AntDepl ...
- fuel一键部署openstack
一.安装环境: 1. 所需物理主机的要求如下 内存:8GB+,推荐16GB:(少于8GB的就免谈了) 磁盘:500GB+: 物理机OS:ubuntu-desktop-amd64 14.04(推荐) 或 ...
- 接口list
List接口介绍 java.util.List 接口继承自 Collection 接口,是单列集合的一个重要分支,习惯性地会将实现了 List 接口的对象称为List集合. List接口特点: 它是一 ...
- Ubuntu下找不到ttyUSB*问题解决
一.硬件连接 确认Ubuntu对USB转串口设备的支持. 1.# lsmod | grep usbserial如果有usbserial,说明系统支持USB转串口. 如果没有,先安装ch340驱动或者c ...
- “理了么”软件特点NABCD个人分析
在这里我就主要对此软件的‘A’(做法)和‘B’(好处)两方面进行分析. “A”:我们的软件为顾客和商家分别提供一种账户,分别登陆后会显示不同的界面. 1.用户界面:拥有“理发店”.“订单“.”和“我的 ...