Гарантированная очистка жесткого диска с помощью 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 |
Вернуться на главную страницу |