通过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.代码实例: ...
随机推荐
- 【转】不需要 Root,也能用上强大的 Xposed 框架:VirtualXposed
如果你喜欢折腾 Android 设备,那么你应该对 Xposed 的大名有所耳闻. 这个第三方框架,让许多 Android 玩家都爱不释手.通过对系统框架的「偷天换日」,它可以修改系统与应用的各种数据 ...
- 2019年3月8日_CCF-A类文章分享
3月8日下午3点开始,王利朋老师主持实验室会议,实验室全体人员参加.首先王利朋老师就基全本子存在的问题进行了再一次的说明以及如何修改部分内容, 其次,王利朋老师对近段时间做了相关工作总结和下阶段任务分 ...
- DotNetCore 3.0 助力 WPF 开发
DotNetCore Is AnyWhere. 前言 Visual Studio 2019 已经正式发布了,DotNetCore 3.0 的正式版也指日可待.在之前的版本中,作为一名基于微软生态的传统 ...
- Linux下的crontab
Ubuntu服务器/var/log下没有cron日志,这里记录一下如何ubuntu server如何查看crontab日志 crontab记录日志 修改rsyslog sudo vim /etc/rs ...
- Swing 混合布局
案例一:Border边境边界 package swing; /** * swing 混合布局 */ import java.awt.*; import javax.swing.*; public cl ...
- 菜鸟学python之程序初体验
作业来源:https://edu.cnblogs.com/campus/gzcc/GZCC-16SE1/homework/2684 1.字符串操作: 解析身份证号:生日.性别.出生地等. def id ...
- laravel 开启定时任务需要操作
1.在xshell 中 crontab -e //编辑任务crontab -l //查看执行中的任务列表 2.在打开的任务中: /home/wwwroot/default 换为自己项目的根路径 vag ...
- springboot 注册dao层 service 层
可以使用三种注解来引入DAO层的接口到spring容器中.1.@Mapper,写在每一个DAO层接口上,如下: 2.@MapperScan和@ComponentScan两者之一.前者的意义是将指定包中 ...
- 【.Net】优秀的开源框架
AutoMapper 官网:http://automapper.org/ github:https://github.com/AutoMapper/AutoMapper 文档:http://autom ...
- Nginx从入门到实践(三)
动静分离 动静分离是将网站静态资源(JavaScript,CSS,img等文件)与后台应用分开部署,提高用户访问静态代码的速度,降低对后台应用访问. 动静分离的一种做法是将静态资源部署在nginx上, ...