通过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.代码实例: ...
随机推荐
- How to Make Fibonacci Confusing
前几天同事发了这么一段代码 (fn => (f => f(f))(f => fn(n => f(f)(n))) )(g => n => [1, 2].indexOf ...
- Elastic Stack-Kibana使用介绍(七)
一.前言 主要来讲述一下Kibana使用以及上生产时候的一些配置,要是大家对这块比较感兴趣我到时候也可以在结合Grafana做一些图表方面的介绍,后面等介绍完Beats以后我去阿里云租几台机器 ...
- UML在代码中的展现
依赖:一个类使用了另外一个类,这种关系是临时的.脆弱的. 如人需要过河,需要船,这时人.过河(船) 中船被当做参数传入,船的实现变化会影响过河方法. 聚合:体现是整体与部分.has-a的关系 ...
- 本地跑 spark ui 报错
java.lang.NoSuchMethodError: javax.servlet.http.HttpServletRequest.isAsyncStarted()Z at org.spark_pr ...
- python中的线程技术
#!/user/bin/env python # @Time :2018/7/7 11:42 # @Author :PGIDYSQ #@File :DaemonTest.py import threa ...
- 【算法】shortest distance
好不容易找到的. #include<iostream> #include<iomanip> #include<cmath> using namespace std; ...
- ArrayList循环遍历并删除元素的几种情况
如下代码,想要循环删除列表中的元素b,该怎么处理? public class ListDemo { public static void main(String[] args) { ArrayList ...
- Java代理模式之Cglib代理
Cglib代理,也叫做子类代理.在内存中构建一个子类对象从而实现对目标对象功能的扩展. CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类.不鼓励直接使用ASM,因 ...
- Adding appsettings.json to a .NET Core console app
This is something that strangely doesn’t seem to be that well documented and took me a while to figu ...
- [2019.04.16] 由Python写成的自动解压脚本
很久很久以前(二十七天吧……大概)被要求写一个脚本来检索并解压磁盘上所有的以特定格式命名的tar文件,于是乎学习和摸鱼就一起开始了. 这次要写的脚本,针对的是这样的文件结构: 文件结构如上图所示 可以 ...