Chapter – 15
数据擦除器编程
简介
我们已经讨论过,当我们从磁盘中删除任何文件时,信息不会从驱动器中完全删除,但它被标记为可用于写入新数据。
当我们格式化驱动器时,驱动器的所有文件和目录信息,例如FAT和根目录条目都被清除,但数据区保持不变,磁盘数据区的任何内容都不会被擦除。使用操作系统删除或格式化的数据将保留在数据区域中,并且可以通过一些数据恢复工作和数据恢复软件进行恢复。
因此,从驱动器中完全清除数据的需要导致需要这种程序来完全清除磁盘中的数据。为此,仅删除文件或仅格式化驱动器是不够的,磁盘上的数据必须被其他一些数据覆盖。
用于彻底清除磁盘数据的程序称为数据清除程序。这些程序在数据区域写入随机字符以覆盖数据并清除之前保存在磁盘上的所有信息。
当数据变得完全不可恢复时
要擦除数据,磁盘的数据区域应该被上面的一些其他数据覆盖,但问题并没有到此结束。更复杂的是,磁驱动器记住已被覆盖的数据的趋势要求数据也被随机数据序列覆盖多次,因此即使使用复杂的数据恢复工具也可能无法恢复
之所以如此,是因为即使在使用一些简单的数据擦除器之后也可以恢复数据的技术如今已经出现。
一些数据擦除产品对数据执行二进制零和二进制覆盖。写入一系列二进制 0 和二进制 1 可以实现最深的覆盖效果,因为这些值分别是最小和最大磁值。
虽然这是一个理想的数据擦除程序的理论,但一般情况下,用随机ASCII字符覆盖数据就足够了。这么说的原因是,复杂的恢复工具和技术的恢复不能用于恢复任何组织的数据以进行常规数据恢复,因为这些技术非常昂贵,即使是单次恢复也要花费数百万美元。不仅如此,而且这些技术仅在世界上少数几个国家/地区可用。
我们将只讨论从磁盘擦除数据的简单数据覆盖。但是,您可以进一步修改相同的程序以编写随机字符,只需稍加努力即可。被这种想法抹去的数据也无法被任何数据恢复软件恢复。
为什么数据擦除如此重要
当我们讨论数据恢复技术时,我们向用户保证,可以通过一些一般或特定的数据恢复工作来恢复数据。但数据恢复并不总是每个人都希望和期望的功能。
可能有很多人或组织总是愿意以不应该以任何方式恢复的方式擦除其磁盘上的数据。在这种情况下,非常敏感的数据可能先前存储在磁盘上,如果落入坏人之手,可能会因滥用信息而损害组织或用户。
众所周知,对硬盘空间的需求日益增加。因此,几乎每个组织每年都会大规模地用新的大容量磁盘替换旧的小容量驱动器。如果这些旧磁盘被不法分子拿到,可能会给该组织带来非常严重的问题。
根据 CNET News.com 发布的消息,2003 年 1 月 16 日,麻省理工学院的学生 Simon Garfinkel 和 Abbi Shelat 一直以研究的名义从网络和其他二手设备上购买旧硬盘销售,以发现人们不愿抹去的大量个人信息。
在以大约 1000 美元的价格购买了 158 个驱动器后,他们设法收集了 5000 多个信用卡号、医疗记录、详细的个人和公司财务信息以及数 GB 的电子邮件、源代码和其他信息。
这对学生将他们的发现汇编成一份题为“对通过的数据的记忆:磁盘清理研究”的报告。发表在 IEEE 安全和隐私 2 月版上。
研究得出的主要观点是,硬盘的二手市场充斥着个人信息,恶意买家很容易冒充他人的身份。
为非破坏性数据擦除器编写程序
无损数据擦除器是一种数据擦除程序,通过它我们可以擦除整个“未分配空间”。磁盘卷,不会以任何方式损害存储在磁盘中的数据。
这种数据擦除器的范围是在您希望擦除磁盘卷的所有未分配空间而存储在卷中的已分配数据应保持不变的情况下。这种类型的数据擦除程序也会擦除已删除文件的数据区域。
程序编码为一种非–接下来给出了破坏性的数据擦除程序:
///// 非破坏性数据擦除程序 \\\\\
#include <stdio.h>
unsigned int file_num=0; /* Provides File Number
During the Auto Creation
of Temporary Data files */
float status=0; /* How Much Disk space is
still Written */
static char dbuf[40000]; /* Data Buffer to write
Temporary Files with */
char file_extension[5]=".ptt";/* Unique Extensions for
Temporary Files */
char temp[5]; /* File Number converted to
String */
char filename[40]; /* Temporary File name */
void main()
{
unsigned int i=0;
clrscr();
while(i<40000)
{
dbuf[i] = ' ';
i++;
}
gotoxy(10,14);cprintf(" MB Still Written...");
while(1)
{
/* 使用唯一名称自动创建临时文件的逻辑 */
strcpy(filename,"TTPT");
itoa(file_num,temp,10);
strcat(filename,temp);
strcat(filename,file_extension);
file_num++;
write_to_temp(filename);
}
} //// End of Main \\\\
///// 将数据写入临时文件的函数 \\\\\
write_to_temp(char *filename)
{
unsigned int i, count=1;
float buf_status=0;
FILE *tt;
if((tt=fopen(filename,"wb"))==NULL)
{
fclose(tt);
printf("\n Error occurred while creating temporary
file, ");
printf("\n Removing temporery Files After KEY BOARD
HIT");
getch();
remove_temp_file();/* Remove All temporary files */
}
while(1)
{
for(i=0;i<50;i++)
{
fprintf(tt,"%s",dbuf);
}
buf_status = (float)((40000*50*count)/512);
status= status+(40000*50);
count++;
gotoxy(10,14);
cprintf("%.0f",(float)(status/1000000));
if(kbhit())
{
fclose(tt);
printf("\n Removing Temporery Files, Please
Wait...");
remove_temp_file();
}
if(buf_status>=10000)
{
fclose(tt);
return;
}
}
}
/* 自动删除临时文件的功能 */
remove_temp_file()
{
int i=0;
for(i=0;i<=file_num;i++)
{
strcpy(filename,"TTPT");
itoa(i,temp,10);
strcat(filename,temp);
strcat(filename,file_extension);
remove(filename);
}
exit(1);
return 0;
}
对程序逻辑和编码的评论:
在这个程序中,我们基本上按照以下两个步骤来擦除磁盘的未分配空间:
- 自动创建临时数据文件:首先我们创建具有唯一名称的临时文件并在其中包含一些数据,直到磁盘卷充满这些临时数据文件。这样一来,逻辑驱动器中所有未分配的数据区域都被临时文件的数据占用,所有未分配的数据都被覆盖。
为此,我选择了 TTPTxxxx.PTT 格式的临时文件的名称,即临时文件的前四个字符为 TTPT,文件的扩展名为 .PTT。这样做是为了给临时文件提供唯一的文件名。
我已经设置了单个临时文件的最大大小,相当于大约 11,718 个扇区数据,但是您可以根据自己的定义。我选择了空间字符“ ” (ASCII 字符 32)将数据填充到临时文件中。然而,也可以使用随机字符代替空格。
- 删除所有临时文件:当逻辑驱动器被临时文件填满时,表示现在所有未分配的数据区域都已被覆盖。现在程序创建的所有临时文件都会自动删除。从而消除了未分配的空间。
在程序的编码中,字符数组filename存储文件名自动生成临时文件,名称不同。
函数 write_to_temp(filename);借助 40,000 字节的数据缓冲区 dbuf,将临时文件最多填充 11,718 个扇区(因为在缓冲区的指定组写入中没有出现 10,000 个扇区)等效数据。一次写入50次数据缓冲区以加快写入速度。
创建临时文件,直到磁盘卷已满并发生文件创建错误。函数 remove_temp_file() 删除程序创建的所有临时文件。
通过这种方式,所有未分配的空间都被清除,而不会损害磁盘卷的数据。
为 破坏性数据擦除器 编写程序:
破坏性数据擦除程序是直接在磁盘表面写入的程序。这种类型的数据擦除程序工作在低于文件系统和操作系统的级别,这意味着所有数据和其他逻辑信息,包括操作系统、文件系统、目录条目以及写入磁盘的所有内容都被擦除。
这些数据擦除程序直接擦除磁盘表面的扇区,并擦除上面写的所有内容。由于包括操作系统在内的所有磁盘数据都丢失了,这些程序称为破坏性数据擦除程序。
这些类型的擦除程序在这种情况下是首选,用户愿意覆盖磁盘上的所有内容,包括操作系统和磁盘上的所有数据。
不过,这种类型的数据擦除程序还有更多好处。由于这些破坏性数据擦除程序完全不受操作系统和文件系统的影响,并且直接在磁盘表面写入,因此它们比非破坏性数据擦除程序要快得多。
另外,如果由于非法存储一些随机数据而造成磁盘上的逻辑坏道,这些逻辑坏道也会随着磁盘的数据被彻底清除。
接下来给出了破坏性数据擦除程序的编码。该程序也被编写为支持大容量磁盘。该程序会擦除连接到计算机的第二个物理硬盘的数据。
///// 破坏性数据擦除程序的编码 \\\\\
#include<stdio.h>
#include<dos.h>
/* getdrivegeometry 函数使用的结构,使用 INT 13H 扩展,函数编号 0x48。 */
struct geometry
{
unsigned int size ; /* (call) size of Buffer */
unsigned int flags ; /* Information Flags */
unsigned long cyl ; /* Number of Physical Cylinders on
Drive */
unsigned long heads ;/* Number of Physical Heads on
Drive */
unsigned long spt ; /* Number of Physical Sectors Per
Track */
unsigned long sectors[2] ; /* Total Number of
Sectors on Drive */
unsigned int bps ; /* Bytes Per Sector */
} ;
/* 磁盘地址包格式的结构,供 writeabsolutesectors 函数使用 */
struct diskaddrpacket
{
char packetsize ; /* Size of Packet,
generally 10H */
char reserved ; /* Reserved (0) */
int blockcount ; /* Number of Blocks to
Transfer */
char far *bufferaddress ; /* address to Transfer
Buffer */
unsigned long blocknumber[2] ; /* Starting Absolute
Block Number */
} ;
///// 获取驱动参数的函数 \\\\\
unsigned long getdrivegeometry (int drive)
{
union REGS i, o ;
struct SREGS s ;
struct geometry g = { 26, 0, 0, 0, 0, 0, 0, 0 } ;
i.h.ah = 0x48 ; /* Function Number 0x48 of INT 13H
Extensions */
i.h.dl = drive; /* Drive Number */
i.x.si = FP_OFF ( (void far*)&g ) ;
s.ds = FP_SEG ( (void far*)&g ) ;
/* 使用段寄存器值调用 INT 13H 扩展的指定功能号 */
int86x ( 0x13, &i, &o, &s ) ;
printf("\n Head = %lu, Sectors Per Track = %lu, Cylinder =
%lu\n",
g.heads,g.spt,g.cyl);
/* 如果获取驱动器几何功能失败,显示错误消息并退出 */
if(g.spt==0)
{
printf("\n Get Drive Geometry Function Fails....");
printf("\n Extensions Not Supported, Press any Key to
Exit...");
getch();
exit(1);
}
return *g.sectors; /* Return The Number of
Sectors on Drive */
}
void main()
{
unsigned long loop=0, Sectors_in_ HDD2=0;
unsigned char buffer[61440]; /* Data buffer of 61440
bytes Equivalent to
120 Sectors */
unsigned long i=0;
char choice;
clrscr();
/* 如果总没有。附加的硬盘数少于两个显示错误消息和退出。 */
if(((char)peekb(0x0040, 0x0075))<2)
{
printf("\n\n You Must Have At least Two Hard Disks
Attached to your Computer To Run This");
printf("\n Program. This Program has been developed
to Wipe the Data of Second Hard Disk.");
printf("\n Press any Key to Exit... ");
getch();
exit(1);
}
Sectors_in_HDD2 = getdrivegeometry (0x81);
printf(" Total Sectors in Second Hard Disk =
%lu\n\n",
Sectors_in_HDD2);
///// 先确认再继续 \\\\\
printf("\n It is A Data Wiping Program, and Writes on
the Surface of the Disk,");
printf("\n After running this program, Data can not
be recovered by any Software,");
printf("\n All The Data in Second Hard Disk will be
lost !!!");
printf("\n Press \'Y\' to Continue, Else any key to
Exit... ");
choice = getche();
switch(choice)
{
case 'y':
case 'Y':
break;
default:
exit(0);
}
gotoxy(10,15);cprintf(" Initializing, Please Wait...");
for(i=0;i<61440;i++)
{
buffer[i]='\0';
}
gotoxy(10,15);cprintf(" ");
gotoxy(10,15);
printf("Currently Wiping Absolute Sector: ");
for(loop=0;loop<= Sectors_in_HDD2;loop=loop+120)
{
writeabsolutesectors (0x81, loop, 120, buffer);
gotoxy(44,15); printf("%ld",loop);
if(kbhit())
{
exit(0);
}
///// 完成时显示消息 \\\\\
printf("\n\n Data wiping is Now Completed, All the Data in
Second Hard Disk is now");
printf("\n Completely Erased, Press any Key to Exit...");
getch();
}
//// 写入绝对扇区的函数 \\\\
int writeabsolutesectors ( int drive, unsigned long sectornumber, int numofsectors, void *buffer )
{
union REGS i, o ;
struct SREGS s ;
struct diskaddrpacket pp ;
pp.packetsize = 16 ; /* Packet Size = 10H */
pp.reserved = 0 ; /* Reserved = 0 */
pp.blockcount = numofsectors ;/* Number of Sectors to
be written */
/* 用于数据缓冲区 */
pp.bufferaddress = (char far*) MK_FP ( FP_SEG((void far*)buffer), FP_OFF((void far*)buffer));
pp.blocknumber[0] = sectornumber ; /* Sector number
to be written*/
pp.blocknumber[1] = 0 ; /* Block number = 0 */
i.h.ah = 0x43 ; /* Function Number */
i.h.al = 0x00 ; /* Write Flags */
i.h.dl = drive ; /* Physical Drive
number */
i.x.si = FP_OFF ( (void far*)&pp ) ; /* ds:si for
buffer Parameters */
s.ds = FP_SEG ( (void far*)&pp ) ; /* ds:si for
buffer Parameters */
/* 使用段寄存器值调用 INT 13H 的指定函数 */
int86x ( 0x13, &i, &o, &s ) ;
if ( o.x.cflag==1)
return 0 ; //failure
else
return 1 ; // success
}
编码评论:
getdrivegeometry函数使用INT 13H Extension, Function Number 0x48来获取磁盘的各种参数。
结构diskaddrpacket用于磁盘地址数据包格式,供writeabsolutesectors函数使用。
函数getdrivegeometry(int drive)是获取磁盘指定物理驱动器号驱动器的DriveParameters。 buffer[61440]是61440字节的数据缓冲区,相当于120个Sector。
(char) peekb(0x0040, 0x0075) 用于查找连接到计算机的硬盘数量,存储在段 0040H:offset 0075H 表示的内存位置。如果连接的硬盘总数少于两个,则显示错误消息并退出。
writeabsolutesectors(0x81,loop,120,buffer)函数用于从loop指定的绝对扇区号开始,一次将数据缓冲区的数据写入120个扇区。
我选择了‘\0’ (NULL 字符,ASCII 代码 0)写入扇区以覆盖数据。但是您可以使用随机字符来覆盖数据。
有关函数 writeabsolutesectors 和 getdrivegeometry 的详细说明,请参阅本书前面给出的章节。
擦除特定文件的数据区
我们讨论了擦除磁盘未分配空间数据或擦除整个磁盘的数据擦除程序。但是如果用户每次删除数据时都愿意擦除数据,那么擦除整个磁盘的未分配空间可能是一个耗时的过程。
我们需要这种类型的数据擦除程序来擦除仅由该特定文件占用的数据区域。为此,我们从 FAT 和 Root 目录条目中获得帮助,以找到该特定文件占用的数据区域
即使在软盘的情况下,如果数据没有碎片,我们也只能借助根目录信息来做到这一点。下表显示了任何文件的根目录条目存储的信息,长度为 32 个字节:
正如我们在根目录条目的目录中看到的那样,我们能够找到文件的开始和结束簇。文件名的第一个字节也可能包含有关文件的一些重要信息。该字节给出的信息可能是以下给出的信息之一:
让我们尝试使用此信息擦除存储在 1.44Mb、3 ½ 中的任何文件的数据。英寸软盘,借助根目录信息。假设软盘中的数据没有碎片,接下来给出的程序从其数据区擦除指定文件的数据:
/* 擦除软盘中指定文件的数据区的程序 */
#include<stdio.h>
#include<dos.h>
///// 读取根目录中 32 字节文件条目的结构 \\\\\
struct root
{
unsigned char filename[8]; /* File name Entry of
8 Bytes */
unsigned char extension[3]; /* Extension of File of
3 Bytes */
unsigned char attribute; /* File Attribute Byte */
unsigned char reserved[10]; /* Reserved Bytes 10 */
unsigned int time; /* Time, 2 Bytes */
unsigned int date; /* Date, 2 Bytes */
unsigned int starting_cluster;/* Starting Cluster of File,
2 Bytes */
unsigned long file_size; /* File Size in Bytes,
4 Bytes */
};
/* 应该以此来读取所有根目录条目 */
//struct root entry[224];
/* 读取根目录扇区中所有 16 个文件条目的结构 */
struct one_root_sector
{
struct root entry[16];
};
struct one_root_sector one;
void main()
{
int result, i, num_sectors,j;
char wipe_buf[512]; /* Data Buffer to be used to wipe
out the data Area of file */
clrscr();
result= absread(0x00, 1, 19, &one); /* Read Absolute Sector
19 (First Sector of Root Directory) */
if (result != 0)
{
perror("Error in Reading Sector, Press any key to
Exit...");
getch();
exit(1);
}
/* 从根目录读取后显示文件信息 */
printf(" FILE NO. FILENAME EXTENSION STARTING CLUSTER
FILESIZE \n\n");
for(i=1;i<16;i++)
{
printf("\n %5d %8.8s %3.3s %5u %10lu ",
i, one.entry[i].filename, one.entry[i].extension,
one.entry[i].starting_cluster, one.entry[i].file_size);
}
//// 获取用户输入以删除文件 \\\\
printf("\n\n Enter The File Number, you Want to Delete and
Wipe out Completely ");
scanf("%d", &i);
if(i<1 || i>15)
{
printf(" \"%d\" is an Invalid Choice..., Press any
Key to Exit...", i);
getch();
exit(1);
}
///// 先确认再继续 \\\\\\
printf("\n You are About to wipe-out,
The File \"%.8s.%s\"",
one.entry[i].filename,
one.entry[i].extension);
printf("\n Do you Want to Continue...(Y/N) ");
switch(getche())
{
case 'y':
case 'Y':
break;
default:
exit(0);
}
///// 计算扇区中文件的大小 \\\\\
num_sectors = one.entry[i].file_size/512;
if((one.entry[i].file_size%512)>0)
{
num_sectors = num_sectors+1;
}
/* 512 个字节的数据缓冲区,包含 512 个 NULL 字符 */
for(j=0;j<512;j++)
{
wipe_buf[j] = '\0';
}
///// 文件的起始扇区 \\\\\
j= one.entry[i].starting_cluster+31;
/* 擦除数据区直到文件扇区结束 */
while(j!=(one.entry[i].starting_cluster +
num_sectors+31) )
{
if((abswrite(0x00, 1, j, &wipe_buf))!=0)
{
printf("\n Error Writing on Disk Sectors");
getch();
exit(0);
}
j++;
}
printf("\n\n File \"%.8s.%.3s\" Deleted !!!" ,
one.entry[i].filename,
one.entry[i].extension);
one.entry[i].attribute = 0; /* Set the File Attribute
to 0 */
one.entry[i].time = 0; /* Wipe The Time information
of File */
one.entry[i].date = 0; /* Wipe The Date information
of File */
one.entry[i].starting_cluster = 0; /* Set the Starting cluster to 0
*/
one.entry[i].file_size = 0; /* Set the file Size to 0 */
one.entry[i].filename[0]=0xE5; /* Give the Deleted
file Status to the File */
///// 将以上信息写入根目录 \\\\\\
result= abswrite(0x00, 1, 19, &one);
if (result != 0)
{
perror("Error in Reading Sector, Press any key to
Exit...");
getch();
exit(1);
}
}
对程序逻辑和编码的评论:
结构root用于读取根目录下32字节的File Entry,结构one_root_sector读取根目录一个Sector中的所有16个File entry
如果要读取所有扇区的根目录信息,应将其作为struct root entry[224];但是我已经编写了程序来分析根目录中只有一个扇区的 16 个条目。
文件的起始扇区计算如下:
j= one.entry[i].starting_cluster+31;
这样做是因为1.44 MB的数据区,3 ½英寸软盘在软盘的前 32 个扇区之后开始。而在规定容量的软盘中,一簇为一扇区。
下表为1.44MB的逻辑图,3½英寸软盘:
程序的输出显示如下:
在这里,我们删除并擦除了文件 PARTBOOT.C 的数据。当我们用 DIR 命令查看软盘的内容时,文件 PARTBOOT.C 并没有显示在那里。当我们进一步执行程序时,被删除文件的入口如下所示:
在这里,角色“” (0xE5),表示文件已被删除。 (文件名的第一个字符见表格)。
如果要为硬盘驱动器编写相同的程序,还必须使用带有根目录的FAT来获取任何文件的数据区信息。
之所以如此,是因为随着时间的推移,硬盘驱动器中的碎片数据率会随着旧文件的删除和新文件的创建而增加。那么驱动器中的任何文件的所有数据簇都没有必要一个接一个地连续停留在数据区中。通过访问 FAT,您可以访问所有这些集群。
Page Modified on: 18/01/2022