468 lines
18 KiB
HTML
468 lines
18 KiB
HTML
|
<!--startcut ==============================================-->
|
||
|
<!-- *** BEGIN HTML header *** -->
|
||
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
|
||
|
<HTML><HEAD>
|
||
|
<title>Securely Erasing a Hard Drive with Perl LG #63</title>
|
||
|
</HEAD>
|
||
|
<BODY BGCOLOR="#FFFFFF" TEXT="#000000" LINK="#0000FF" VLINK="#0000AF"
|
||
|
ALINK="#FF0000">
|
||
|
<!-- *** END HTML header *** -->
|
||
|
|
||
|
<CENTER>
|
||
|
<A HREF="http://www.linuxgazette.com/">
|
||
|
<H1><IMG ALT="LINUX GAZETTE" SRC="../gx/lglogo.png"
|
||
|
WIDTH="600" HEIGHT="124" border="0"></H1></A>
|
||
|
|
||
|
<!-- *** BEGIN navbar *** -->
|
||
|
<IMG ALT="" SRC="../gx/navbar/left.jpg" WIDTH="14" HEIGHT="45" BORDER="0" ALIGN="bottom"><A HREF="nielsen.html"><IMG ALT="[ Prev ]" SRC="../gx/navbar/prev.jpg" WIDTH="16" HEIGHT="45" BORDER="0" ALIGN="bottom"></A><A HREF="index.html"><IMG ALT="[ Table of Contents ]" SRC="../gx/navbar/toc.jpg" WIDTH="220" HEIGHT="45" BORDER="0" ALIGN="bottom" ></A><A HREF="../index.html"><IMG ALT="[ Front Page ]" SRC="../gx/navbar/frontpage.jpg" WIDTH="137" HEIGHT="45" BORDER="0" ALIGN="bottom"></A><A HREF="http://www.linuxgazette.com/cgi-bin/talkback/all.py?site=LG&article=http://www.linuxgazette.com/issue63/nielsen2.html"><IMG ALT="[ Talkback ]" SRC="../gx/navbar/talkback.jpg" WIDTH="121" HEIGHT="45" BORDER="0" ALIGN="bottom" ></A><A HREF="../faq/index.html"><IMG ALT="[ FAQ ]" SRC="./../gx/navbar/faq.jpg"WIDTH="62" HEIGHT="45" BORDER="0" ALIGN="bottom"></A><A HREF="nielsen3.html"><IMG ALT="[ Next ]" SRC="../gx/navbar/next.jpg" WIDTH="15" HEIGHT="45" BORDER="0" ALIGN="bottom" ></A><IMG ALT="" SRC="../gx/navbar/right.jpg" WIDTH="15" HEIGHT="45" ALIGN="bottom">
|
||
|
<!-- *** END navbar *** -->
|
||
|
<P>
|
||
|
</CENTER>
|
||
|
|
||
|
<!--endcut ============================================================-->
|
||
|
|
||
|
<H4 ALIGN="center">
|
||
|
"Linux Gazette...<I>making Linux just a little more fun!</I>"
|
||
|
</H4>
|
||
|
|
||
|
<P> <HR> <P>
|
||
|
<!--===================================================================-->
|
||
|
|
||
|
<center>
|
||
|
<H1><font color="maroon">Securely Erasing a Hard Drive with Perl</font></H1>
|
||
|
<H4>By <a href="mailto:articles@gnujobs.com">Mark Nielsen</a></H4>
|
||
|
</center>
|
||
|
<P> <HR> <P>
|
||
|
|
||
|
<!-- END header -->
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
<H3>Contents</H3>
|
||
|
|
||
|
<ol>
|
||
|
|
||
|
<li>
|
||
|
<a href="#Introduction">Introduction</a></li>
|
||
|
<li>
|
||
|
<a href="#problems">What problems I have.</a></li>
|
||
|
|
||
|
<li>
|
||
|
<a href="#perl">The Perl Script</a></li>
|
||
|
|
||
|
<li>
|
||
|
<a href="#Conclusion">Conclusion</a></li>
|
||
|
|
||
|
<li>
|
||
|
<a href="#REF">References</a></li>
|
||
|
</ol>
|
||
|
|
||
|
<h3>
|
||
|
<a NAME="Introduction"></a>Introduction</h3>
|
||
|
When moving from Ohio to California, GNUJobs.com had some hard drives
|
||
|
(along with other hardware and software)
|
||
|
which were to be donated to
|
||
|
<a href="http://www.colug.net">COLUG</a> (Central Ohio Linux Users Group).
|
||
|
They needed to be deleted
|
||
|
before they were donated.
|
||
|
2 out of the 3 hard drives had bad sectors on them, and the third
|
||
|
I ended up using as a hard drive for testing purposes, like creating
|
||
|
this article, so I ended up not giving any away. Still, I will need to
|
||
|
wipe a hard drive in the future, so I created this Perl script (which I
|
||
|
will later convert to Python and make it have more options).
|
||
|
<p>
|
||
|
The goal of this Perl script is to just delete the hard drive at /dev/hdb
|
||
|
(the slave drive on the Primary IDE controller) since I have a hard drive
|
||
|
removeable kit there. I want it to delete all partitions, create one
|
||
|
partition that takes up the whole hard drive, and then fill up the
|
||
|
hard drive with garbage data (including some random encrypted data
|
||
|
just to ruin a hacker's day trying to find out what the data is).
|
||
|
|
||
|
<h3>
|
||
|
<a NAME="problems"></a>The Problems</h3>
|
||
|
|
||
|
Here is a list of problems I had and how I solved them:
|
||
|
<ol>
|
||
|
<li> <font size= +1>How do I get it to delete all the partitions?</font>
|
||
|
<p>I remember researching many different options to alter partitions on
|
||
|
a hard drive, and doing it manually yielded the best results. I had used
|
||
|
a Perl Expect script to automate the fdisk program (fdisk partitions hard
|
||
|
drives in Linux) in the past, and I decided to continue to do it
|
||
|
that way. I believe
|
||
|
there are better alternatives for the simple task of deleting all the
|
||
|
partitions, like sfdisk and others, but if one solution covers all
|
||
|
possibilities with 100% power and flexibility, I usually just stick to
|
||
|
one way of doing things so that I don't have to remember too many things
|
||
|
and if it ever gets more complicated, I don't have to learn anything new.
|
||
|
|
||
|
<p>Thus, I used Expect code to simulate a user typing in the commands
|
||
|
for fdisk. The Expect code deleted all the partitions and then
|
||
|
it created one big partition.<p></li>
|
||
|
<li><font size=+1>How do I fill up the hard drive with garbage data?</font>
|
||
|
<p> Just deleting partitions isn't enough to delete the data. I want
|
||
|
to overwrite all old data
|
||
|
with garbage to make sure the previous data was deleted.
|
||
|
I used sfdisk to get the size of the partition that fdisk created.
|
||
|
Then, I created a loop which would continuously print garbage data
|
||
|
until the amount printed was equal or greater than the size of the partition.
|
||
|
<p></li>
|
||
|
<li>
|
||
|
<font size=+1> How do I put binary data on the hard
|
||
|
drive to confuse a hacker?</font>
|
||
|
<P> I created random binary data using the
|
||
|
random function and "chr" function in Perl. Then, I encrypted the random
|
||
|
data using the Perl Blowfish module. If someone manages to decrypt the
|
||
|
data, it will still look like garbage and confuse them. I wanted to encrypt
|
||
|
the data so that it didn't look purely random in a mathematical sense.
|
||
|
<p> </li>
|
||
|
<li> <font size=+1> How do I reformat the big partition?</font>
|
||
|
<P> This was easy. I just used a simple "mkfs" command.
|
||
|
</ol>
|
||
|
|
||
|
<h3>
|
||
|
<a NAME="perl"></a>The Perl Script</h3>
|
||
|
The version of Perl I was using for this Perl script was out of date.
|
||
|
I was using Perl 5.005_03 and I believe Perl is up to 5.6 as of 1/2001.
|
||
|
<p>
|
||
|
There are a lot of things I need to enhance to make this
|
||
|
script more user friendly. There should be a lot more error checking,
|
||
|
considering how dangerous this script is, and prompts to ask a user
|
||
|
if they really want to do so stuff. I am waiting until I restart my MILAS
|
||
|
project (which will be written in Python) before I make this script better.
|
||
|
It was only to get me through moving from Columbus to the Bay Area.
|
||
|
<p> I have commented a lot of the code, so hopefully a novice Perl
|
||
|
programmer can understand most of what I am trying to do.
|
||
|
|
||
|
<A HREF="misc/nielsen2/Wipe_It.pl.txt">(Text version of this listing)</A>
|
||
|
|
||
|
<pre>
|
||
|
#!/usr/bin/perl
|
||
|
|
||
|
##### Things to do
|
||
|
# 1. Make sure we create a brand new directory for temporary mounting
|
||
|
# in order to avoid security risks in case someone is logged in.
|
||
|
# 2. Use perl functions to handle a lot of the system calls.
|
||
|
# 3. Let it autodetect hard drives, and floppy drives, and only perform
|
||
|
# actions on unmounted hard drives and floppy drives.
|
||
|
#####
|
||
|
|
||
|
use strict;
|
||
|
use Expect;
|
||
|
use Crypt::Blowfish;
|
||
|
|
||
|
#-----------------------------------------------
|
||
|
my $Junk;
|
||
|
### Set the drive to the slave drive on the Primary IDE controller.
|
||
|
my $Drive = "hdb";
|
||
|
|
||
|
### Let us do a lot of random stuff, and get the last line from the
|
||
|
### /etc/passwd file to make it really random, assuming one person
|
||
|
### has been added to the computer.
|
||
|
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);
|
||
|
|
||
|
### Done making up random key, now create Blowfish Encryption object.
|
||
|
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>;
|
||
|
|
||
|
### Get the list of mounted partitions on the drive we want to wipe out
|
||
|
my @Mounted = `df`;
|
||
|
@Mounted = grep($_ =~ /\/dev\/hdb/, @Mounted);
|
||
|
### Foreach mounted partition, umount it
|
||
|
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;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
### Start the expect script, which will simulate someone doing this
|
||
|
### commands manually.
|
||
|
my $Fdisk = Expect->spawn("/sbin/fdisk /dev/$Drive");
|
||
|
|
||
|
### Get a list of mounted partitions by printing the partition table
|
||
|
print $Fdisk "p\n";
|
||
|
my $match=$Fdisk->expect(30,"Device Boot Start");
|
||
|
|
||
|
my $Temp = $Fdisk->exp_after();
|
||
|
my @Temp = split(/\n/, $Temp);
|
||
|
## Get the lines that tell us about the partitions
|
||
|
my @Partitions = grep($_ =~ /^\/dev\//, @Temp);
|
||
|
|
||
|
## Foreach line, delete the partition
|
||
|
foreach my $Line (reverse @Partitions)
|
||
|
{
|
||
|
## Get the /dev/hdb part, and its number
|
||
|
my ($Part,$Junk) = split(/[\t ]/, $Line,2);
|
||
|
my $No = $Part;
|
||
|
$No =~ s/^\/dev\/$Drive//;
|
||
|
|
||
|
print "Deleting no $Drive $No\n";
|
||
|
|
||
|
## Delete command
|
||
|
print $Fdisk "d\n";
|
||
|
$match=$Fdisk->expect(30,"Partition number");
|
||
|
|
||
|
## Which partition number to delete
|
||
|
print $Fdisk "$No\n";
|
||
|
$match=$Fdisk->expect(30,"Command (m for help):");
|
||
|
}
|
||
|
|
||
|
$Fdisk->clear_accum();
|
||
|
|
||
|
### If we had partitions, write changes, or otherwise, just end it
|
||
|
if (@Partitions < 1) {print $Fdisk "q\n"; $Fdisk->expect(2,":");}
|
||
|
else
|
||
|
{
|
||
|
print $Fdisk "w\n";
|
||
|
$Fdisk->expect(30,"Command (m for help):");
|
||
|
}
|
||
|
|
||
|
#-------------------------------
|
||
|
## Get the geometry of the hard drive
|
||
|
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;}
|
||
|
|
||
|
### Create a new expect script to simulate a person using fdisk
|
||
|
my $Fdisk = Expect->spawn("/sbin/fdisk /dev/$Drive");
|
||
|
|
||
|
#### Tell fdisk to create new partition
|
||
|
print $Fdisk "n\n";
|
||
|
$Fdisk->expect(5,"primary");
|
||
|
|
||
|
### Tell it the new partition should be a primary partition
|
||
|
print $Fdisk "p\n";
|
||
|
$Fdisk->expect(5,":");
|
||
|
|
||
|
### Which partition, number 1
|
||
|
print $Fdisk "1\n";
|
||
|
$Fdisk->expect(5,":");
|
||
|
|
||
|
### Start at cylinder 1
|
||
|
print $Fdisk "1\n";
|
||
|
$Fdisk->expect(5,":");
|
||
|
|
||
|
### Go to the end
|
||
|
print $Fdisk "$Cyl\n";
|
||
|
$Fdisk->expect(5,":");
|
||
|
|
||
|
### Write and save
|
||
|
print $Fdisk "w\n";
|
||
|
$Fdisk->expect(30,"Command (m for help):");
|
||
|
|
||
|
#------------------------------------------
|
||
|
### Format the partition and mount it
|
||
|
|
||
|
my $Partition = "/dev/$Drive" . "1";
|
||
|
my $Result = system ("mkfs -t ext2 $Partition");
|
||
|
if ($Result > 0) {print "Error making partition, aborting.\n"; exit;}
|
||
|
|
||
|
### There should be better error checking here
|
||
|
system "umount /tmp/WIPE_IT";
|
||
|
system "rm -rf /tmp/WIPE_IT";
|
||
|
system "mkdir -p /tmp/WIPE_IT";
|
||
|
system "chmod 700 /tmp/WIPE_IT";
|
||
|
|
||
|
## See if we can mount the new partition.
|
||
|
my $Result = system ("mount $Partition /tmp/WIPE_IT");
|
||
|
if ($Result > 0) {print "Error mounting drive, aborting.\n"; exit;}
|
||
|
system "chmod 700 /tmp/WIPE_IT";
|
||
|
|
||
|
#--------------------------------
|
||
|
### Now create the file and stop when we hit the size.
|
||
|
|
||
|
my $Count = 0;
|
||
|
my $Written_Size = 0;
|
||
|
|
||
|
### Open up a new file.
|
||
|
open(FILE,">>/tmp/WIPE_IT/Message.txt");
|
||
|
### If someone actually wants to screw around with your hard drive,
|
||
|
### let us play with them and waste their time by adding a teaser.
|
||
|
my $Ran = rand 259200000; # between now and ten years ago (approx)
|
||
|
($Ran, $Junk) = split(/\./, $Ran, 2);
|
||
|
## New date minus random number of seconds
|
||
|
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";
|
||
|
|
||
|
### Create random number plus 25000
|
||
|
my $Ran = rand 25000;
|
||
|
($Ran, $Junk) = split(/\./, $Ran, 2);
|
||
|
$Ran = $Ran + 25000;
|
||
|
|
||
|
### Create an array of numbers which we will use most of the time.
|
||
|
my @Blank = (1..$Ran);
|
||
|
### Take the array and make into a string.
|
||
|
my $Blank = "@Blank";
|
||
|
### Empty the array to free up memory.
|
||
|
@Blank = ();
|
||
|
my $B_Length = length $Blank;
|
||
|
|
||
|
### Let us get the amount of real space we have for the partition
|
||
|
my @Temp = `df`;
|
||
|
@Temp = grep($_ =~ /^$Partition/, @Temp);
|
||
|
my $Line = $Temp[0];
|
||
|
my ($Junk,$Blocks,@Junk) = split(/\s+/, $Line,4);
|
||
|
### We are assuming 1k blocks.
|
||
|
my $Size = $Blocks*1000;
|
||
|
|
||
|
## While the file we have written is less than the size of the
|
||
|
## partition, print some more data.
|
||
|
while ($Written_Size < $Size)
|
||
|
{
|
||
|
$Count++;
|
||
|
|
||
|
### 9 out of ten times, we just want to print blank spaces to hurry
|
||
|
### up printing. One out of ten times, print garbage binary.
|
||
|
my $Ran = rand (10);
|
||
|
if ($Ran > 1)
|
||
|
{
|
||
|
print FILE $Blank;
|
||
|
$Written_Size = $Written_Size + $B_Length;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
## This part makes a long string (upto 10000 bytes) of random data.
|
||
|
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;
|
||
|
}
|
||
|
## This parts encrypts the random data 8 bytes at a time.
|
||
|
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 the encrypted random data to file.
|
||
|
print FILE $Encrypted;
|
||
|
$Length = length $Encrypted;
|
||
|
|
||
|
$Written_Size = $Written_Size + $Length;
|
||
|
my $Rest = $Size - $Written_Size;
|
||
|
print "$Size - $Written_Size = $Rest to go\n";
|
||
|
}
|
||
|
|
||
|
### At every 500 prints, start saving to a new file.
|
||
|
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; }
|
||
|
|
||
|
### Let us reformat the partition. Doesn't delete data, just removes it
|
||
|
### from the directory.
|
||
|
my $Result = system ("mkfs -t ext2 $Partition");
|
||
|
if ($Result > 0) {print "Error making partition, aborting.\n"; exit;}
|
||
|
|
||
|
|
||
|
</pre>
|
||
|
|
||
|
<h3>
|
||
|
<a NAME="Conclusion"></a>Conclusion</h3>
|
||
|
Using Expect was not necessary (other programs could have solved
|
||
|
the simple problems I had). Using Blowfish was not necessary. As a matter
|
||
|
of fact, the whole darn script was way too long if you just wanted to
|
||
|
wipe a hard drive and fill it with blanks. However, I wanted to use
|
||
|
fdisk because I always want to use fdisk, Expect is such a powerful tool,
|
||
|
it is good to let people see how it works, and putting random garbage
|
||
|
encrypted binary data in to confuse a hacker is just an extra touch.
|
||
|
<p>
|
||
|
I don't understand the complete complexity of hard drives, so I am not
|
||
|
sure if there are residual data left on the hard drive.
|
||
|
For my purposes, and my level of security, it does exactly what
|
||
|
I need. As I develop MILAS more, I am sure there will be tighter checks
|
||
|
and enhancements to delete all data off of a hard drive.
|
||
|
<p>
|
||
|
I tend to look forward in time trying to anticipate things which might be
|
||
|
needed in the future, which always causes a programmer to work more than is
|
||
|
required for the project at hand. However, the mood struck me, and I like
|
||
|
the direction the script is going, and so, it doesn't bother me to write
|
||
|
up this article on an airplane flight. Making something cool doesn't
|
||
|
wear me out, unlike having to do work for someone else, which is real work.
|
||
|
|
||
|
|
||
|
<h3>
|
||
|
<a NAME="REF"></a>References</h3>
|
||
|
|
||
|
<ol>
|
||
|
<li><a href="http://www.perl.com">Perl.com</a> website.
|
||
|
<li><a href="http://www.perl.com/CPAN-local/modules/by-category/21_File_Handle_Input_Output/Expect/Expect-1.10.readme">Expect Perl Module</a></li>
|
||
|
<li><a href="http://www.perl.com/CPAN-local/modules/by-category/14_Security_and_Encryption/Crypt/Crypt-Blowfish-2.06.readme">Blowfish Perl Module</a></li>
|
||
|
<li><a href="http://www.gnujobs.com/Articles/14/Wipe_It.html">Original site for this article. - http://www.gnujobs.com/Articles/14/Wipe_It.html</a>
|
||
|
(any updates will be placed here)</li>
|
||
|
</ol>
|
||
|
|
||
|
|
||
|
<BLOCKQUOTE><EM>
|
||
|
[You can also use /dev/random or /dev/urandom to overwrite
|
||
|
a disk. See<BR>
|
||
|
<A HREF="../issue60/lg_answer60.html#tag/9">The Answer Gang: "Classified Disk - Low-level Format"</A>
|
||
|
in issue 60. But it doesn't do encryption. -Mike.]
|
||
|
</EM></BLOCKQUOTE>
|
||
|
|
||
|
|
||
|
<!-- *** BEGIN copyright *** -->
|
||
|
<P> <hr> <!-- P -->
|
||
|
<H5 ALIGN=center>
|
||
|
|
||
|
Copyright © 2001, Mark Nielsen.<BR>
|
||
|
Copying license <A HREF="../copying.html">http://www.linuxgazette.com/copying.html</A><BR>
|
||
|
Published in Issue 63 of <i>Linux Gazette</i>, Mid-February (EXTRA) 2001</H5>
|
||
|
<!-- *** END copyright *** -->
|
||
|
|
||
|
<!--startcut ==========================================================-->
|
||
|
<HR><P>
|
||
|
<CENTER>
|
||
|
<!-- *** BEGIN navbar *** -->
|
||
|
<IMG ALT="" SRC="../gx/navbar/left.jpg" WIDTH="14" HEIGHT="45" BORDER="0" ALIGN="bottom"><A HREF="nielsen.html"><IMG ALT="[ Prev ]" SRC="../gx/navbar/prev.jpg" WIDTH="16" HEIGHT="45" BORDER="0" ALIGN="bottom"></A><A HREF="index.html"><IMG ALT="[ Table of Contents ]" SRC="../gx/navbar/toc.jpg" WIDTH="220" HEIGHT="45" BORDER="0" ALIGN="bottom" ></A><A HREF="../index.html"><IMG ALT="[ Front Page ]" SRC="../gx/navbar/frontpage.jpg" WIDTH="137" HEIGHT="45" BORDER="0" ALIGN="bottom"></A><A HREF="http://www.linuxgazette.com/cgi-bin/talkback/all.py?site=LG&article=http://www.linuxgazette.com/issue63/nielsen2.html"><IMG ALT="[ Talkback ]" SRC="../gx/navbar/talkback.jpg" WIDTH="121" HEIGHT="45" BORDER="0" ALIGN="bottom" ></A><A HREF="../faq/index.html"><IMG ALT="[ FAQ ]" SRC="./../gx/navbar/faq.jpg"WIDTH="62" HEIGHT="45" BORDER="0" ALIGN="bottom"></A><A HREF="nielsen3.html"><IMG ALT="[ Next ]" SRC="../gx/navbar/next.jpg" WIDTH="15" HEIGHT="45" BORDER="0" ALIGN="bottom" ></A><IMG ALT="" SRC="../gx/navbar/right.jpg" WIDTH="15" HEIGHT="45" ALIGN="bottom">
|
||
|
<!-- *** END navbar *** -->
|
||
|
</CENTER>
|
||
|
</BODY></HTML>
|
||
|
<!--endcut ============================================================-->
|