windows下修改磁盘扇区数据

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zfs2008zfs/article/details/54288643

Windows系统提供了文件系统,通常应用程序读写文件都是调用CreateFile函数来实现,不会直接读写磁盘数据,但某些特殊目的中,我们需要读取磁盘扇区数据,查看是否包含某些关键字,并且有可能擦除这些关键字(类似于Winhex和diskgenius),在编写一个磁盘扇区修改的程序时,应注意以下几点:
1、获取磁盘句柄
获取磁盘句柄依然用CreateFile函数,例子如下:

 HANDLE hDev=CreateFile("\\\\.\\C:",GENERIC_READ,FILE_SHARE_READ || FILE_SHARE_WRITE,0,OPEN_EXISTING,0,0);//打开逻辑磁盘
 HANDLE hDev=CreateFile("\\\\.\\PHYSICALDRIVE0",GENERIC_READ,FILE_SHARE_READ || FILE_SHARE_WRITE,0,OPEN_EXISTING,0,0);//打开物理磁盘

注意,对于系统盘(C盘)来说,即使我们在CreateFile的第二个参数传入GENERIC_READ,第三个参数要设置为FILE_SHARE_READ || FILE_SHARE_WRITE,由于系统在运行时要不停的读写系统盘(因为页文件保存在系统盘),如果第三个参数不这样设置,很可能获取句柄失败。

2、在Win7及以上版本中,应用层只能通过CreateFile获取读磁盘扇区的权限,是无法直接修改磁盘扇区的。在调用WriteFile之前需调用DeviceIOControl传入FSCTL_LOCK_VOLUME将卷锁定,然后可以写扇区。但MSDN上对FSCTL_LOCK_VOLUME的介绍有这么一句话:“Locks a volume if it is not in use. If the specified volume is a system volume or contains a page file, the operation fails.”所以,在Win7以上,想要读写系统磁盘的扇区,恐怕只能在内核层去实现了。
当然,如果想在应用层修改系统盘扇区,可以在程序实现后,运行winPE系统,在winPE环境下运行程序,这样就可以修改原来系统盘的扇区。这样做调试程序非常麻烦,winPE下没有开发环境,只能打印出足够多的信息判断问题出在哪,然后切换回原来的开发环境进行修改。

3、在各个版本中,应用层都可以调用CreateFile获取读磁盘扇区的权限,只要第三个参数设置正确都能成功。但在读扇区的时候要注意,通常磁盘扇区的大小都是512个字节,每次读取的数量必须是512的整数倍。
写磁盘扇区也是这样,必须读取一个或几个扇区的值,修改其中某几个字节,然后把数据重新写回去。

4、调用ReadFile读取扇区之后,文件位置会往后移,如果直接调用WriteFile修改数据,修改的已不是刚刚读取的数据。举个例子,比如调用ReadFile从0x1000处读取512个字节,那么文件句柄指向的文件位置已经移到了0x1200处,如果此时调用WriteFile把读取的512个字节写回去,从0x1200起的512字节就会被覆盖,这一点千万要注意!
为了解决这个问题,就需要用到SetFilePointer函数。

SetFilePointer(hDev,-512,NULL,FILE_CURRENT);

5、注意SetFilePointer函数的用法
SetFilePointer函数的第二个参数和第三个参数是有符号数,负数表示向文件开始的位置移动,正数表示向文件结尾的位置移动
文件的大小用字节表示,一个32位数最多只能表示4GB的文件位置,因此SetFilePointer函数的第二个参数和第三个参数合起来表示一个64位有符号数

如果已知扇区索引,如何根据扇区索引定位到对应的文件位置呢?
DWORD dwIndex;//表示扇区索引
 LARGE_INTEGER distance;
 distance.LowPart =dwIndex*512;
 distance.HighPart =(dwIndex*512)>>32;
 SetFilePointer(hDev,distance.LowPart,&distance.HighPart,FILE_BEGIN);
 这样写就不需要考虑第三个参数是否传入NULL了。

6、磁盘扇区扫描的细节
现在电脑硬盘的容量都很大,动辄1TB、2TB,折算成扇区就更多了。在进行磁盘扇区的扫描时,如果一个一个扇区读取的话,效率很低,全盘扫描需要几小时甚至几十个小时,提高扫描效率很有必要。方法有以下两个:

  • 每次调用ReadFile读取多个扇区,比如64个或者128个
  • 采用多线程的方法,每个线程中都调用ReadFile,但需要注意线程同步的问题

扇区扫描时,还可能出现关键字跨扇区的问题。比如搜索”hello”这个关键字,可能出现”he”在一个扇区的最后两个字节,”llo”在下一个扇区的前三个字节,解决这个问题,笔者想到的方法有两种:

  • 每次调用ReadFile读取64个扇区,然后调用SetFilePointer往文件开始方向移动一个扇区
  • 用两个循环队列,其中一个每次调用ReadFile读取64个扇区,另一个每次调用ReadFile读取63个扇区,这样可能会有疏漏

猜你喜欢

转载自blog.csdn.net/zfs2008zfs/article/details/54288643