稀疏文件(Sparse File)探秘

稀疏文件在NTFS中用于节省存储空间,尤其在处理大文件如虚拟机磁盘、数据库和备份文件时。它们包含大量声明为0但未实际写入磁盘的数据。通过设置文件属性,Windows可以识别并处理这些稀疏区域,提高写入和搜索速度。然而,这种技术也可能带来磁盘空间误导和安全问题,如大文件复制和磁盘配额计算。
摘要由CSDN通过智能技术生成

稀疏文件主要由0构成:
在这里插入图片描述
为何会有这种奇怪的文件呢?有些软件在创建文件的时候,会先给文件提供空间,然后再逐渐把数据写入文件,这些文件包括:

数据库文件:
aOracle Temporary Tablespaces
MS SQL Backup files
虚拟机文件
VMDK NTFS Metadata 文件
$BadCluster
$ Usnjrnl:$j

举例说明:你创建了一个60GB的虚拟机磁盘文件,在正常情况下,该文件要占用60GB的物理磁盘空间。

但在大多数情况下虚拟磁盘并未写满,例如下图,虚拟机内的操作系统本身仅占用了9.29GB,还有50.7GB的剩余空间,如果在物理硬盘上创建完整60GB的虚拟磁盘文件未免太浪费了:
在这里插入图片描述
我们可以采取一种技巧减少物理磁盘的占用:将60GB的虚拟磁盘分为“真正写入”的部分和“不写入”的部分,前者把已用空间里的数据真正写入物理磁盘,未用空间的数据不写入磁盘但作声明:剩下的数据都是0。

这样一来,虚拟机用户虽然看到了完整的磁盘容量,但只有一部分数据真正写入了物理磁盘。随着写入数据量的增大,虚拟磁盘所占的物理空间也逐渐增大,直至声明的最大空间。

所以稀疏文件包含有大量的0,但文件系统并没有真正使用磁盘上的物理块来存储这些0,那么NTFS是怎样做到这点的呢?我们使用三个工具来分析:fsutil、FlexHex和Aactive@ Disk Editor,前者是Windows自带,需要以管理员身份进入命令提示符运行。
1、创建一个4MB的文件,以16进制给出文件大小:

fsutil file createnew silly.txt

该文件的数据是被完全填充的,用​FlexHex可以看到0被真实的分配了,因为是黑色的字体:
在这里插入图片描述
但是保存大量的0是很不划算的,因为数据写入磁盘需要时间,还要占用成本高昂的存储空间。所以我们可用这样做:在文件的属性里设置某种标志,指明哪些部分是全0的(不用写入磁盘),哪些部分是真实写入的。这样做的好处是:

避免写入0到磁盘,提升了写入速度
只搜索真正写入数据的部分,不再搜索稀疏部分,提升了搜索速度。

2、将silly.txt转换为稀疏文件:
fsutil sparse setflag silly.txt

我们用Aactive@ Disk Editor证实这点,右键选择silly.txt文件:
在这里插入图片描述
之后点选左边的Attribute $10 -> $STANDARD_INFORMATION​ ->File Permissions,可见Sparase File位已经被置1:
在这里插入图片描述
再进入Attribute $80 -> Flags,Sparse位也被置1:
在这里插入图片描述
再往下进入DATA -> Data run:
在这里插入图片描述

可见本文件被分配了1024个“簇”(也叫分配单元),每个簇是4096字节(默认,在格式化时选择),那么总容量就是1024*4096=4194304,也就是4MB字节。

3、置该文件的前3MB为0(不写入磁盘),后1M是真实数据,写入磁盘:

fsutil sparse setrange silly.txt 0 0x300000

查询稀疏范围。可见稀疏部分是文件开头之后的0x300000字节,后面紧跟长度为0x100000字节的真实写入的数据:
fsutil sparse queryrange silly.txt
在这里插入图片描述
如何证明这点?再次运行Aactive@ Disk Editor,重新载入文件,发现有了2个Data Run(之前只有一个):
在这里插入图片描述
第一个Data Run告诉你该文件的稀疏部分被分配了768个簇(下方的Sparse位是Yes),768×4096=3145728,正好是3MB。

​第二个Data Run告诉你真正写入的部分被分配了256个簇,256×4096=1048576,正好是1MB。
这与Windows资源管理器的磁盘占用统计是吻合的,该文件只占用了1M的物理空间:
在这里插入图片描述

​FlexHex也能证明这点,显示的全0部分为灰色字体,表明为稀疏部分,真实部分为黑底白字,右下角的”SPARSE“被突出显示:
在这里插入图片描述
现在你知道了什么是稀疏文件,这就是。

​NTFS在读写稀疏文件时是这样处理的:稀疏属性对用户/应用程序透明。当读稀疏文件,NTFS将返回0以替代文件中的稀疏部分;当写入稀疏文件,NTFS需要被告知该文件是稀疏的,并标记0区域。

有些应用程序能够识别稀疏文件,有些则不能。比如用记事本打开稀疏文件,修改几个字符再保存,该文件就变为普通文件了,复制或移动稀疏文件也是如此,因为稀疏文件需要特殊的处理方式。Windows提供了一些这方面的API:

  • GetVolumeInformationA
    -lpFileSystemFlags 输出参数包含一个标志列表
    –FILE_SUPPORTS_SPARSE_FILES
  • DeviceloControl
    –FSCTL_SET_SPARSE
    –FSCTL_SET_ZERO_DATA
    –FSCTL QUERY ALLOCATED

稀疏文件提升了大文件的写入速度和搜索速度,但也带来了风险,因为磁盘剩余空间的数值会误导你。比如下面这个批处理,它会创建500个大小为50MB的稀疏文件,总容量为24.4GB,但却一个字节也没有存入物理磁盘:

for /l %%x in (1,1,500) do (
echo %%x
fsutil file createnew %%x.txt 0x3200000
fsutil sparse setflag %%x.txt
fsutil sparse setrange %%x.txt 0 0x3200000
)

在这里插入图片描述
你可以把这些文件考入剩余空间为100MB的磁盘,但如果其中某几个文件逐渐增大,磁盘就会被写满。另外,如果你使用的空间被“磁盘配额”限制,那也是按照24.4GB计算的,哪怕没有写入任何数据。
在这里插入图片描述
最后,我们讨论一下稀疏文件的安全性问题。2016年的一篇​​文章​​提到一个有趣的反分析技巧。你可以将一个小巧的可执行文件转换为一个巨大的稀疏文件,且不会影响到可执行文件的加载。但如果有人试图复制或移动该文件进行分析,那么他将得到一个臃肿的文件。

 // SparseMaker.cpp - taken from https://www.scriptjunkie.us/2016/08/defying-analysis-with-sparse-malware/
#include "pch.h"
#include <Windows.h>
#include <iostream>

using namespace std;
void main(int argc, char** argv) {
  if (argc < 3) {
    cerr << "Usage: " << argv[0] << " file size" << endl;
    return;
  }
  HANDLE h = CreateFileA(argv[1], GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
  if (h == INVALID_HANDLE_VALUE) {
    cerr << "Cannot open file" << endl;
    return;
  }
  DWORD outlen;
  DeviceIoControl(h, FSCTL_SET_SPARSE, NULL, 0, NULL, 0, &outlen, NULL);
  LARGE_INTEGER newlen;
  newlen.QuadPart = atoll(argv[2]);
  SetFilePointer(h, newlen.LowPart, &newlen.HighPart, FILE_BEGIN);
  SetEndOfFile(h);
  CloseHandle(h);
}

sparsemaker.exe helloworld.exe 1073741824

这样就把一个几十k的小文件转换为1GB大小的稀疏文件。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值