章 – 15
データワイパーのプログラミング
序章
ディスクからファイルを削除するときに、情報がドライブから完全に消去されるわけではありませんが、新しいデータを書き込むことができるようにマークされていることについては、すでに説明しました。
ドライブをフォーマットすると、FATやルートディレクトリエントリなど、ドライブのファイルとディレクトリのすべての情報が消去されますが、データ領域は変更されず、ディスクのデータ領域から何も消去されません。オペレーティングシステムを使用して削除またはフォーマットされたデータは、そのままデータ領域に残り、一部のデータ回復作業とデータ回復ソフトウェアを使用して回復できます。
したがって、ドライブからデータを完全に削除する必要があるため、ディスクからデータを完全に消去するようなプログラムが必要になります。これを行うには、ファイルを削除したり、ドライブをフォーマットしたりするだけでは不十分ですが、ディスク上のデータを他のデータで上書きする必要があります。
ディスクからデータを完全に消去するために使用されるプログラムは、データ消去プログラムと呼ばれます。これらのプログラムは、データ領域にランダムな文字を書き込んで、データを上書きし、以前にディスクに保存されたすべての情報を消去します。
データが完全に回復不能になったとき
データを消去するには、ディスクのデータ領域を他のデータで上書きする必要がありますが、問題はここで終わりではありません。さらに複雑なことに、磁気ドライブが上書きされたデータを記憶する傾向があるため、高度なデータ回復ツールを使用してもデータが回復されないように、データはランダムなデータシーケンスによって数回上書きされる必要があります。
これは、いくつかの単純なデータワイパーを使用した後でもデータを回復できるテクノロジーが現在利用可能であるためです。
一部のデータ消去製品は、データに対して2進ゼロと2進1の上書きを実行します。一連の2進ゼロと2進ゼロを書き込むと、これらの値がそれぞれ最小と最大の磁気値であるため、最も深い上書き効果が得られます。
これは理想的なデータ消去プログラムの理論ですが、一般的には、ランダムなASCII文字による上書きデータで十分です。その理由は、高度な回復ツールとテクノロジーによる回復は、単一の回復でも非常に高価で数百万のコストがかかるため、定期的なデータ回復のために組織のデータを回復するために使用できないためです。これだけでなく、これらのテクノロジーも世界のいくつかの国でのみ利用可能です。
ディスクからデータを消去するための単純なデータ上書きについてのみ説明します。ただし、同じプログラムをさらに変更して、少しの努力でランダムな文字を書くことができます。このアイデアによって消去されたデータは、データ回復ソフトウェアによっても回復できません。
データ消去が非常に重要である理由
データ回復手法について説明するとき、一般的または特定のデータ回復作業でデータを回復できることをユーザーに保証します。しかし、データリカバリは、必ずしもすべての人にとって望ましい、期待される機能であるとは限りません。
いかなる方法でも回復されないように、ディスクのデータを常に消去することをいとわない人や組織がたくさんあるかもしれません。このような場合、非常に機密性の高いデータが以前にディスクに保存されていた可能性があり、悪用された場合、情報を悪用して組織やユーザーに損害を与える可能性があります。
ご存知のとおり、ハードディスクに必要なスペースは日々増え続けています。この結果、ほとんどすべての組織で、古い小容量ドライブが毎年大規模な新しい大容量ディスクに置き換えられています。これらの古いディスクを悪意のある人が入手した場合、その組織にとって非常に深刻な問題が発生する可能性があります。
CNET News.comが発行したニュースによると、2003年1月16日、マサチューセッツ工科大学の学生であるSimonGarfinkelとAbbiShelatは、研究の名目で、Webやその他の中古品から古いハードドライブを購入しています。膨大な量の個人情報を明らかにするための販売は、人々がわざわざ消去することはありません。
158台のドライブを約1,000米ドルで購入した後、5000を超えるクレジットカード番号、医療記録、詳細な個人および企業の財務情報、数ギガバイトの電子メール、ソースコード、その他の情報を収集することができました。
これらの学生のペアは、彼らの調査結果を「 "渡されたデータの記憶:ディスクサニテーションの研究" IEEE SecurityandPrivacyの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;
}
プログラムのロジックとコーディングに関するコメント:
このプログラムでは、基本的に次の2つの手順に従って、ディスクの未割り当て領域を消去します。
- 一時データファイルを自動的に作成する:最初に、一意の名前で一時ファイルを作成し、ディスクボリュームがこれらの一時データファイルでいっぱいになるまで、いくつかのデータを含めます。これにより、論理ドライブのすべての未割り当てデータ領域が一時ファイルのデータによって占有され、すべての未割り当てデータが上書きされます。
これを行うために、TTPTxxxx.PTT形式の一時ファイルの名前を選択しました。つまり、一時ファイルの最初の4文字はTTPTであり、ファイルの拡張子は.PTTです。これは、一時ファイルに一意のファイル名を提供するために行われます。
単一の一時ファイルの最大サイズを設定しました。これは約11,718セクターのデータに相当しますが、必要に応じて定義できます。スペースキャラクターを選びました “ ” (ASCII文字32)データを一時ファイルに入力します。ただし、スペースの代わりにランダムな文字を使用することもできます。
- すべての一時ファイルを削除する:論理ドライブが一時ファイルでいっぱいになると、割り当てられていないすべてのデータ領域が上書きされたことを示します。これで、プログラムによって作成されたすべての一時ファイルが自動的に削除されます。このようにして、割り当てられていないスペースを一掃することができます。
プログラムのコーディングでは、文字配列filenameにファイル名が格納され、異なる名前の一時ファイルが自動的に生成されます。
関数write_to_temp(filename); 40,000バイトのデータバッファdbufを使用して、一時ファイルを最大11,718セクター(バッファの指定されたグループ書き込みで10,000セクターの発生がないため)の同等のデータで埋めます。書き込みを高速化するために、一度に50倍のデータバッファが書き込まれます。
一時ファイルは、ディスクボリュームがいっぱいになり、ファイル作成エラーが発生するまで作成されます。関数remove_temp_file()は、プログラムによって作成されたすべての一時ファイルを削除します。
このようにして、ディスクボリュームのデータを損なうことなく、未割り当て領域がすべて消去されます。
破壊的データワイパー用のプログラムの作成:
破壊的なデータ消去プログラムは、ディスクの表面に直接書き込むプログラムです。このタイプのデータ消去プログラムは、ファイルシステムやオペレーティングシステムよりも低いレベルで動作します。つまり、OS、ファイルシステム、ディレクトリエントリ、ディスクに書き込まれたすべてのデータやその他の論理情報が消去されます。
これらのデータ消去プログラムは、ディスクの表面のセクターを直接消去し、ディスクに書き込まれたすべてのものを消去します。オペレーティングシステムを含むディスクのすべてのデータが失われるため、これらのプログラムは破壊的データ消去プログラムと呼ばれます。
これらのタイプのワイププログラムは、ユーザーがオペレーティングシステムやディスク上のすべてのデータを含む、ディスク上のすべてのものを上書きすることをいとわない場合に適しています。
ただし、このタイプのデータ消去プログラムには、さらにいくつかの利点があります。これらの破壊的なデータ消去プログラムは、OSやファイルシステムから完全に解放され、ディスクの表面に直接書き込むため、非破壊的なデータ消去プログラムよりもかなり高速です。
また、ランダムデータの不正な保存が原因でディスク上に論理的な不良セクタが作成された場合、これらの論理的な不良セクタもディスクのデータで完全に消去されます。
次に、破壊的なデータ消去プログラムのコーディングを示します。プログラムは、大きなサイズのディスクもサポートするように作成されています。プログラムは、コンピューターに接続されている2番目の物理ハードディスクのデータを消去します。
/////破壊的なデータ消去プログラムのコーディング \\\\\
#include<stdio.h>
#include<dos.h>
/ * INT 13H拡張機能、関数番号0x48を使用するgetdrivegeometry関数で使用される構造。 */
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 ) ;
/ *セグメントレジスタ値を使用してINT13H拡張の指定された機能番号を呼び出します */
int86x ( 0x13, &i, &o, &s ) ;
printf("\n Head = %lu, Sectors Per Track = %lu, Cylinder =
%lu\n",
g.heads,g.spt,g.cyl);
/ * get drive Geometry関数が失敗した場合、エラーメッセージを表示して終了します */
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();
/ *合計がない場合。接続されているハードディスクの数は、エラーメッセージの表示と終了の2つ未満です。 */
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 */
/ *セグメントレジスタ値を使用してINT13Hの指定された関数を呼び出します */
int86x ( 0x13, &i, &o, &s ) ;
if ( o.x.cflag==1)
return 0 ; //failure
else
return 1 ; // success
}
コーディングに関するコメント:
構造ジオメトリは、INT 13H拡張機能、関数番号0x48を使用するgetdrivegeometry関数によって使用され、ディスクのさまざまなパラメータを取得します。
構造体diskaddrpacketは、ディスクアドレスパケット形式用であり、writeabsolutesectors関数によって使用されます。
関数getdrivegeometry(int drive)は、ディスクで指定された物理ドライブ番号ドライブのドライブパラメータを取得することです。バッファ[61440]は、120セクターに相当する61440バイトのデータバッファです。
(char)peekb(0x0040、0x0075)は、セグメント0040H:offset0075Hで表されるメモリ位置に格納されているコンピュータに接続されているハードディスクの数を見つけるために使用されます。接続されているハードディスクの総数が2つ未満の場合は、エラーメッセージを表示して終了します。
writeabsolutesectors(0x81、loop、120、buffer)関数は、loopで指定された絶対セクター番号から一度に120セクターのデータバッファーのデータを書き込むために使用されます。
I chose ‘\0’ (NULL文字、ASCIIコード0)データを上書きするセクターに書き込みます。ただし、ランダムな文字を使用してデータを上書きできます。
関数writeabsolutesectorsおよびgetdrivegeometryの詳細な説明については、この本の前半に記載されている章を参照してください。
特定のファイルのデータ領域を消去する
ディスクの未割り当て領域のデータを消去したり、ディスク全体を消去したりするデータ消去プログラムについて説明しました。ただし、ユーザーがデータを削除するたびにデータをワイプする場合は、ディスクの未割り当て領域全体をワイプするのに時間がかかる可能性があります。
このタイプのデータ消去プログラムは、その特定のファイルによってのみ占有されているデータ領域を消去するために必要です。これを行うために、FATおよびルートディレクトリエントリの助けを借りて、その特定のファイルが占めるデータ領域を見つけます
フロッピーの場合でも、データが断片化されていなければ、ルートディレクトリ情報の助けを借りてのみ断片化することができます。次の表は、任意のファイルについて、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 */
};
/ *すべてのルートディレクトリエントリを読み取るには、これを使用する必要があります */
//構造体ルートエントリ[224];
/ *ルートディレクトリの1つのセクターにある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個のNULL文字を含む512バイトのデータバッファ */
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バイトのファイルエントリを読み取るために使用され、構造体one_root_sectorは、ルートディレクトリの1つのセクター内の16個のファイルエントリすべてを読み取ります。
ルートディレクトリ情報のすべてのセクターを読み取りたい場合は、それをstruct root entry [224]と見なす必要があります。ただし、ルートディレクトリの1つのセクターのみの16エントリを分析するプログラムを作成しました。
ファイルの開始セクターは次のように計算されています。
j = one.entry [i] .starting_cluster + 31;
これは、1.44MBのデータ領域のために行われます。 3 ½ インチフロッピーディスクは、フロッピーディスクの最初の32セクターの後に起動します。また、指定された容量のフロッピーディスクでは、1つのクラスターが1つのセクターに属します。
次の表は、1.44MBの論理マップを示しています。 3½ インチフロッピーディスク:
プログラムの出力は次のように表示されます。
ここでは、ファイルPARTBOOT.Cのデータを削除してワイプしました。 DIRコマンドでフロッピーの内容を見ると、PARTBOOT.Cファイルは表示されていません。プログラムをさらに実行すると、削除されたファイルのエントリが次のように表示されます。
ここで、キャラクター “” (0xE5), ファイルが削除されたことを表します。 (ファイル名の最初の文字については、表を参照してください)。
ハードディスクドライブ用に同じプログラムを作成する場合は、ルートディレクトリでFATを使用して、ファイルのデータ領域の情報を取得する必要もあります。
これは、古いファイルが削除され、新しいファイルが作成されるにつれて、ハードディスクドライブ内の断片化されたデータの割合が時間とともに増加するためです。その場合、ドライブ内のファイルのすべてのデータクラスタがデータ領域に連続して1つずつ留まる必要はありません。 FATにアクセスすることで、これらすべてのクラスターにアクセスできます。