|
Гарантированная очистка жесткого диска с помощью Perl
Автор: © Mark Nielsen
|
|
Содержание: ВведениеКогда GNUJobs.com переезжал из Огайо в Калифорнию, несколько жестких дисков вместе с другим компьютерным железом планировалось подарить Группе пользователей Linux Центрального Огайо COLUG (Central Ohio Linux Users Group). Перед этим предполагалось стереть на дисках все данные. На 2 из 3 жестких дисков имелись сбойные сектора, а третий я сделал своим "тестовым полигоном" для таких целей, как написание этой статьи, так что все диски в результате остались у меня. Но, поскольку мне все равно предстоит очищать диски в будущем, я написал скрипт на Perl'е (позднее я перепишу его на Python'е и добавлю еще опций). В задачу скрипта входила простая очистка /dev/hdb (ведомый диск первичного контроллера IDE), т. к. к нему у меня был подключен набор для работы с удаляемыми жесткими дисками. Я планировал удалить все имеющиеся разделы, создать единственный раздел максимального размера и заполнить его всяким мусором, в том числе и обрывками зашифрованных данных (для того, чтобы занять на денек-другой предполагаемого хакера выяснением того, что же было на диске). ПроблемыВот как я решал возникающие проблемы:
Сам скриптДля этого скрипта я воспользовался устаревшей версией. У меня был Perl 5.005_03, а к январю 2001 г. доступны версии Perl до 5.6 включительно. Можно много еще чего сделать, чтобы сделать скрипт удобнее в работе. Учитывая, как много может натворить эта маленькая программка, следовало бы добавить дополнительные проверки для исключения ошибок и запрашивать у пользователя подтверждение тех или иных действий. Но эти усовершенствования я отложил до того момента, когда я возобновлю проект MILAS (который будет написан на Python'е). Этот же скрипт писался с единственной целью помочь мне при переезде из Коламбуса к Заливу (Bay Area). Код я обильно откомментировал, так что надеюсь, даже новичок в Perl'е сможет понять большую часть того, что я пытался достичь. (Исходный текст можно взять здесь). Версия исходного текста с оригинальными (непереведенными) комментариями находится здесь
#!/usr/bin/perl
##### Предполагается:
# 1. Убедимся, что мы создаем совершенно новую директорию
# для монтирования с тем, чтобы избежать проблем с безопасностью,
# если кто-либо еще из пользователей вошел в систему
# 2. Использовать функции Perl для выполнения множества системных вызовов
# 3. Автоматически определяем жесткие и гибкие диски и выполняем действия
# только над не-примонтированными устройствами
#####
use strict;
use Expect;
use Crypt::Blowfish;
#-----------------------------------------------
my $Junk;
### Настроимся на ведомый диск первичного контроллера IDE.
my $Drive = "hdb";
### Выполним множество случайных действий,
### а в завершение возьмем последнюю строку /etc/passwd
### в предположении, что на компьютере был добавлен один пользователь
my $time = time();
my $Ran = rand($time);
my $Ran = rand(10000000000000);
my $LastLine = `tail -n 1 /etc/passwd`; chomp $LastLine;
$LastLine = substr ($LastLine,0,30);
my $Blowfish_Key = $LastLine . $Ran . $time;
$Blowfish_Key = substr ($Blowfish_Key,0,20);
while (length ($Blowfish_Key) < 56)
{
$Blowfish_Key .= $Ran = rand($time);
}
$Blowfish_Key = substr ($Blowfish_Key,0,56);
### Случайный ключ готов, создаем объект Blowfish Encryption.
my $Blowfish_Cipher = new Crypt::Blowfish $Blowfish_Key;
#------------------------------------
system "clear";
print "This will wipe out the hard drive on Drive /dev/$Drive\n";
print "Press enter to continue\n";
my $R = <STDIN>;
### Получить список смонтированных разделов
my @Mounted = `df`;
@Mounted = grep($_ =~ /\/dev\/hdb/, @Mounted);
### Размонтируем смонтированные разделы
foreach my $Mount (@Mounted)
{
my ($Partition,$Junk) = split(/\s+/, $Mount,2);
print "Unmounting $Partition\n";
my $Result = system ("umount $Partition");
if ($Result > 0)
{
print "ERROR, unable to umount $Partition, aborting Script, Error = $Result\n";
exit;
}
}
### Запуск скрипта Expect, который эмулирует выполнение команд вручную
my $Fdisk = Expect->spawn("/sbin/fdisk /dev/$Drive");
### Из вывода fdisk'ом таблицы разделов получаем их список
print $Fdisk "p\n";
my $match=$Fdisk->expect(30,"Device Boot Start");
my $Temp = $Fdisk->exp_after();
my @Temp = split(/\n/, $Temp);
## Выделяем строки с информацией о разделах
my @Partitions = grep($_ =~ /^\/dev\//, @Temp);
## Для каждой такой строки -- удаляем раздел
foreach my $Line (reverse @Partitions)
{
## Получаем раздел /dev/hdb и его номер
my ($Part,$Junk) = split(/[\t ]/, $Line,2);
my $No = $Part;
$No =~ s/^\/dev\/$Drive//;
print "Deleting no $Drive $No\n";
## Команда на удаление
print $Fdisk "d\n";
$match=$Fdisk->expect(30,"Partition number");
## Указываем номер удаляемого раздела
print $Fdisk "$No\n";
$match=$Fdisk->expect(30,"Command (m for help):");
}
$Fdisk->clear_accum();
### Если разделы еще остались -- записываем изменения, если нет -- выходим
if (@Partitions < 1) {print $Fdisk "q\n"; $Fdisk->expect(2,":");}
else
{
print $Fdisk "w\n";
$Fdisk->expect(30,"Command (m for help):");
}
#-------------------------------
## Получаем геометрию жесткого диска
my $Geometry = `/sbin/sfdisk -g /dev/$Drive`;
my ($Junk, $Cyl, $Junk2, $Head, $Junk3, $Sector,@Junk) = split(/\s+/,$Geometry);
if ($Cyl < 1)
{print "ERROR: Unable to figure out cylinders for drive. aborting\n"; exit;}
### Новый скрипт Expect для эмуляции действий пользователя
my $Fdisk = Expect->spawn("/sbin/fdisk /dev/$Drive");
#### Велим fdisk создать новый раздел
print $Fdisk "n\n";
$Fdisk->expect(5,"primary");
### Новый раздел будет первичным
print $Fdisk "p\n";
$Fdisk->expect(5,":");
### Какой раздел? Номер 1
print $Fdisk "1\n";
$Fdisk->expect(5,":");
### От забора (цилиндра 1)...
print $Fdisk "1\n";
$Fdisk->expect(5,":");
### ...и до обеда (конца диска)
print $Fdisk "$Cyl\n";
$Fdisk->expect(5,":");
### Запишем и сохранимся
print $Fdisk "w\n";
$Fdisk->expect(30,"Command (m for help):");
#------------------------------------------
### Отформатируем раздел и смонтируем его
my $Partition = "/dev/$Drive" . "1";
my $Result = system ("mkfs -t ext2 $Partition");
if ($Result > 0) {print "Error making partition, aborting.\n"; exit;}
### Тут надо бы добавить всяких проверок...
system "umount /tmp/WIPE_IT";
system "rm -rf /tmp/WIPE_IT";
system "mkdir -p /tmp/WIPE_IT";
system "chmod 700 /tmp/WIPE_IT";
## Посмотрим, получилось ли смонтировать.
my $Result = system ("mount $Partition /tmp/WIPE_IT");
if ($Result > 0) {print "Error mounting drive, aborting.\n"; exit;}
system "chmod 700 /tmp/WIPE_IT";
#--------------------------------
### Создаем файло и пишем до упора.
my $Count = 0;
my $Written_Size = 0;
### Открываем новый файл.
open(FILE,">>/tmp/WIPE_IT/Message.txt");
### И если кому-то придет в голову проиграться с нашим винтом,
### то пусть разомнутся с этой завлекушечкой, мы тоже любим играться.
my $Ran = rand 259200000; # эдак между сейчас и 10 лет тому назад...
($Ran, $Junk) = split(/\./, $Ran, 2);
## Новая дата минус случайное число секунд...
my $Date = `date --date '-$Ran seconds'`;
print FILE "DATE CREATED $Date\n";
my $Ran = rand 50;
($Ran, $Junk) = split(/\./, $Ran, 2);
$Ran = $Ran + 10;
print FILE "This document is extremely secure. It is a violation to let
any unauthorized persons read it. Known password holders need to
apply Method $Ran in order to decrypt binary data.\n";
### Случайное число плюс 25000
my $Ran = rand 25000;
($Ran, $Junk) = split(/\./, $Ran, 2);
$Ran = $Ran + 25000;
### Создаем массив случайных чисел для частого употребления.
my @Blank = (1..$Ran);
### Превращаем его в строку.
my $Blank = "@Blank";
### Освободим массив для экономии памяти.
@Blank = ();
my $B_Length = length $Blank;
### Получаем доступное место на разделе
my @Temp = `df`;
@Temp = grep($_ =~ /^$Partition/, @Temp);
my $Line = $Temp[0];
my ($Junk,$Blocks,@Junk) = split(/\s+/, $Line,4);
### Предполагаем, что блок равен 1k.
my $Size = $Blocks*1000;
## Если записанный файл меньше размера раздела
## запишем еще немного.
while ($Written_Size < $Size)
{
$Count++;
### 9 раз из десяти печатаем пустые данные для ускорения процесса
### а один раз печатаем цифровой мусор.
my $Ran = rand (10);
if ($Ran > 1)
{
print FILE $Blank;
$Written_Size = $Written_Size + $B_Length;
}
else
{
## А здесь сделаем длинную (до 10000 байт) строку случайных символов.
my $Garbage = "";
my $Length = rand(10000);
($Length, $Junk) = split(/\./, $Length, 2);
for (my $i = 0; $i < $Length; $i++)
{
my $Ran = rand 256;
($Ran, $Junk) = split(/\./, $Ran, 2);
$Garbage .= chr $Ran;
}
## Здесь зашифруем случайную строку по 8 байт за раз.
my $Temp = $Garbage;
my $Encrypted = "";
while (length $Temp > 0)
{
while (length $Temp < 8) {$Temp .= "\t";}
my $Temp2 = $Blowfish_Cipher->encrypt(substr($Temp,0,8));
$Encrypted .= $Temp2;
if (length $Temp > 8) {$Temp = substr($Temp,8);} else {$Temp = "";}
}
### Запишем зашифрованные случайные данные в файл.
print FILE $Encrypted;
$Length = length $Encrypted;
$Written_Size = $Written_Size + $Length;
my $Rest = $Size - $Written_Size;
print "$Size - $Written_Size = $Rest to go\n";
}
### Каждые 500 вызовов print начинаем новый файл.
if ($Count =~ /500$/)
{
close FILE;
open(FILE,">>/tmp/WIPE_IT/$Count");
}
}
close FILE;
#----------------------------------------------------
my $Result = system ("umount $Partition");
if ($Result > 0) {print "Error unmounting partition $Partition, aborting.\n"; exit; }
### Отформатируем раздел. Это не сотрет данные,
### а просто удалит их из каталога.
my $Result = system ("mkfs -t ext2 $Partition");
if ($Result > 0) {print "Error making partition, aborting.\n"; exit;}
ЗаключениеИспользовать Expect необязательно -- другие программы справятся с этой простой задачей не хуже. Использовать Blowfish тоже не обязательно. По правде, весь этот скрипт и так слишком длинён для того, чтобы просто очистить жесткий диск и заполнить его пустыми данными. Однако я хотел использовать fdisk, потому что я всегда хочу использовать fdisk, а Expect такой мощный инструмент, что хочется показать всем, как он работает. А создание оцифрованного и зашифрованного шума для смущения незадачливых хакеров -- просто завершающий штрих. Я не понимаю жесткие диски во всей их сложности, так что я не уверен в том, что после моей процедуры на диске совсем не останется следов от данных. Для моих целей (и на моем уровне требований к безопасности), скрипт делает в точности то, что мне нужно. По мере дальнейшего развития MILAS наверняка добавятся более жесткие проверки и усовершенствования для того, чтобы все данные на диске были уничтожены. Я склонен заглядывать вперед, и стараюсь предугадать то, что может понадобиться в будущем, а это всегда заставляет программиста работать над своим проектом больше, чем продиктовано непосредственной необходимостью. Однако я был в настроении, мне нравилось направление, в котором развивается скрипт, и меня не затруднило написать эту статью, пока я летел в самолете. Работа над какой-нибудь крутой штуковиной меня не утомляет, в отличие от необходимости сделать работу для кого-то другого (это-то и есть настоящая работа). Источники
|
|
Copyright © 2001, Mark Nielsen. Copying license http://www.linuxgazette.com/copying.html Published in Issue 63 of Linux Gazette, Mid-February (EXTRA) 2001 |
|
Вернуться на главную страницу |