Click here to Skip to main content
15,884,176 members
Articles / Programming Languages / VBScript
Article

Erasing data on free space on NTFS partitions

Rate me:
Please Sign up or sign in to vote.
4.13/5 (5 votes)
19 Sep 20048 min read 61.8K   691   19   6
A VBScript that overwrites free disk space to prevent file recovery on a NTFS partition, by creating a file with random data and (mostly) the size of the free space.

Introduction

This week I ordered a new computer to replace one of my private machines at home. Because I'm still indecisive what to do with the old one and selling it is one option, I thought it might be a good idea to erase all the old data on the two hard disks. The problem is that format does not really do this, leaving the possibility to potentially restore files later. So, I wrote this little script that you would not want to use at an intelligence service (you will see why later) but which should be sufficient for home use. Because it is a command line script, it must be run by CScript.

CScript Overwrite.vbs [path] [filename]

Global Declarations

The basic idea is to create a file which consumes all free space on a partition and contains only random data. So, my script starts with some default settings and then calls Main.

VBScript
Option Explicit

Const sDefaultDir = "C:\"
Const sDefaultFilename = "overwrite.garbage"
Const lStartBlockSize = 32768

Call Main

Option Explicit forces you to explicitly declare variables you want to use. This helps to avoid using variables you accidentally created by a typo, and those errors can be really hard to debug. The Constants contain the default directory, default filename, and the block size. Directory and filename should be self-explanatory. The block size defines how many bytes are written with each call of TextStream.Write in CreateGarbageFile. After experimenting with some block sizes, I decided to use a 32 kilobyte block (= 32 * 1024 bytes = 215 bytes) because a larger block would take too much time to be created on a 200 MHz processor, and a 32 Kb block takes a few seconds to be created even on a 2 GHz processor.

Sub Main()

The Sub Main does all the work regarding the fulfillment of the prerequisites and also starts off with some local declarations and initializations.

The variables sPath and sFilename will store the actual values which can be eventually the default values or values passed as command line parameters. The other variables will be used for various objects. ShowStartMsg is a simple method that just uses WScript.Echo to display a static start-up message.

The last two lines are more interesting. The Arguments property of the WScript object returns a collection of the command line parameters. A FileSystemObject object is needed for all the file I/O operations like processing paths, dealing with drives, or creating a file. The object has to be created prior to use, by calling CreateObject which is also a method of WScript.

VBScript
Dim sPath, sFilename
Dim oArgs, oFS, oDrive, oRegExp, oMatches

ShowStartMsg

Set oArgs = WScript.Arguments
Set oFS = CreateObject("Scripting.FileSystemObject")

The first check I perform is whether there is only one command line parameter and this parameter equals /?. I get the number of elements in the WshArguments collection by calling Count, and access elements by using their index (starting with 0), so usage does not differ from any other collection. If both conditions are true, ShowHelp is called to display some information on how to use the script. The parameter value true tells ShowHelp to cancel the execution of the script by calling WScript.Quit.

VBScript
If oArgs.Count = 1 Then
  If oArgs(0) = "/?" Then
    ShowHelp true
  End If
End If

Then I check if more than two parameters were passed from the command line. Because the script supports only two parameters (which are both optional), this would be an invalid input. The Sub ShowMsg is defined as Sub ShowMsg(sMsg, bShowHelpHint, bExit) where sMsg is a string to be displayed on the console. bShowHelpHint determines whether a hint on how to start Overwrite.vbs to get help will be shown, and bExit determines if the script should quit after the message was displayed.

VBScript
If oArgs.Count > 2 Then
  ShowMsg "ERROR: Invalid command line parameters" & _
          " (too many parameters specified)", true, true
End If

After that, I check if a path has been provided by the user. If he or she did, I call GetAbsolutePathName for further use. GetAbsolutePathName translates relative path names into absolute path names. It does not matter for making the script work but always using absolute path names when telling the user what is done makes the script's output unambiguous. If no path was specified, I initialize sPath with an empty string.

VBScript
If oArgs.Count > 0 Then
  sPath = oFS.GetAbsolutePathName(oArgs(0))
Else
  sPath = ""
End If

The next step is to verify that the given path exists. FolderExists does this for me. All I need to do is to pass the path I want to verify, in this case the content of sPath. A backslash is amended to a user-specified path in case it was omitted by the user. This allows getting a valid path to the file we are going to write by combining the path and the filename (sPath & sFilename).

VBScript
If oFS.FolderExists(sPath) Then
  WScript.Echo "Checking folder " & Chr(34) & sPath & Chr(34) & ": OK"
  If Right(sPath, 1) <> "\" Then
    sPath = sPath & "\"
  End If
Else
  WScript.Echo "Checking folder " & Chr(34) & sPath & Chr(34) & ": FAILED"
  sPath = sDefaultDir
  WScript.Echo "INFO: Using default folder " & Chr(34) & sPath & Chr(34)
End If

After we know that the path is okay, the filename needs verification. This is done in three steps. The first step is to analyze the command line parameters. If a filename was specified by the user and it is not empty, this filename is copied into sFilename (an empty filename provided by the user is treated as an error condition). If no filename was specified, the default filename (see Declarations) is copied into sFilename.

VBScript
If oArgs.Count = 2 Then
  sFilename = oArgs(1)
  If sFilename = "" Then
    ShowMsg "ERROR: Filename must not be empty", true, true
  End If
Else
  sFilename = sDefaultFilename
  WScript.Echo "INFO: Using default filename " & Chr(34) & sFilename & Chr(34)
End If

Second, we need to ensure that the filename does not contain any invalid characters. This is done by using a regular expression. After creating an instance of the RegExp class, I set the Pattern property to my regular expression which contains all the forbidden characters. Then, I call Execute and pass sFilename as the parameter. The return value is a Matches collection. Because I just want to know if my filename contains any invalid characters, I don't bother with checking the collection's content but only with how many elements it contains. If it does not contain any elements at all, the filename is valid.

VBScript
Set oRegExp = new RegExp
oRegExp.Pattern = "[\\\/\:\*\?\" & Chr(34) & "\<\>\|]"
Set oMatches = oRegExp.Execute(sFilename)
If oMatches.Count = 0 Then
  WScript.Echo "Validating filename: OK"
Else
  WScript.Echo "Validating filename: FAILED"
  ShowMsg "ERROR: Filename must not contain the following characters:"_
    & " \ / : * ? " & Chr(34) & " < > |", true, true
End If

Because the user might enter the name of an existing file and I really don't want to delete user data or program files, I finally make sure that the specified file does not exist by calling the FileExists method of FileSystemObject.

VBScript
If oFS.FileExists(sPath & sFilename) = False Then
  WScript.Echo "Ensuring that file " & Chr(34) & sFilename & Chr(34) &_
    " does not exist: OK"
Else
  WScript.Echo "Ensuring that file " & Chr(34) & sFilename & Chr(34) &_
    " does not exist: FAILED"
  ShowMsg "ERROR: File " & Chr(34) & _
          sPath & sFilename & Chr(34) & " already exists", true, true
End If

Now, I make sure that my target is a NTFS partition. Why is that? Because I wanted to write a simple script which just writes one big file. Using FAT file systems implies a maximum file size of 4 GB (in case of FAT32). The free space however might be larger than that and I didn't want to create multiple files (in fact I don't even use FAT file systems anymore) to keep the script as simple as possible.

To get this information, we need a Drive object. And to get that, we simply call GetDrive and pass the return value of GetDriveName as its parameter (passing the whole path would lead to an exception when it does not point to the root directory). Then we just check if the FileSystem property is set to NTFS.

VBScript
Set oDrive = oFS.GetDrive(oFS.GetDriveName(sPath))
If UCase(oDrive.FileSystem) = "NTFS" Then
  WScript.Echo "Checking for NTFS: OK"
Else
  WScript.Echo "Checking for NTFS: FAILED"
  ShowMsg "ERROR: " & oDrive.FileSystem & _
          " file system not supported", true, true
End If

The last thing we want to prevent is that someone starts writing random data over a network. Therefore, we use the DriveType property of our Drive object to get the drive type, and only proceed if it is a fixed or a removable drive.

VBScript
Select Case oDrive.DriveType
  Case 1, 2
    WScript.Echo "Checking drive type: OK"
  Case Else
    WScript.Echo "Checking drive type: FAILED"
    Select Case oDrive.DriveType
      Case 3
        ShowMsg "ERROR: Network drives are not supported", true, true
      Case 4
        ShowMsg "ERROR: CD-ROM drives are not supported", true, true
      Case 5
        ShowMsg "ERROR: RAM Disk drives are not supported", true, true
      Case Else
        ShowMsg "ERROR: Unkown drives are not supported", true, true
    End Select
End Select

The last check eventually verifies that there is any free space we are allowed to write to. This is not necessarily the free space of the partition if quotas are active. This information is returned by the FreeSpace property.

VBScript
If oDrive.FreeSpace > 0 Then
  WScript.Echo "Checking for free space: OK"
Else
  WScript.Echo "Checking for free space: FAILED"
  WScript.Echo "INFO: No free space available (no action required)"
  ShowMsg "INFO: Exiting Overwrite Script...", false, true
End If

Now that we know that everything is in place, we can actually start filling the drive with junk. Main only writes some output to the console, calls CreateGarbageFile, and deletes the garbage file by calling DeleteFile.

VBScript
WScript.Echo "Creating garbage file " & Chr(34) & _
             sPath & sFilename & Chr(34) & "..."
CreateGarbageFile sPath & sFilename, oFS
WScript.Echo "Garbage file successfully created!"
WScript.Echo "INFO: " & oDrive.AvailableSpace & _
             " byte(s) remained which could not be overwritten"
WScript.Echo "Deleting garbage file..."
oFS.DeleteFile sPath & sFilename
WScript.Echo "Garbage file successfully deleted!"
WScript.Echo "Exiting Overwrite Script..."
WScript.Quit

Sub CreateGarbageFile(sAbsFilename, oFS)

CreateGarbageFile expects two parameters. sAbsFilename is the filename including the full path and oFS is a FileSystemObject object. The local variable bSngByteBlock is set to true when the function enters the single byte block mode, while sBlock is the string that is written to disk. The Drive object was introduced when discussing Main, and oFile is a TextStream object. This object is used to actually write data to disk and is created by calling the CreateTextFile method of the FileSystemObject class. The first parameter of that call is the filename. The second one tells the function not to overwrite files and the third one means that 8-bit ASCII will be used (the alternative would be Unicode).

VBScript
Dim bSngByteBlock
Dim sBlock
Dim oFile, oDrive

bSngByteBlock = false
Set oDrive = oFS.GetDrive(oFS.GetDriveName(sAbsFilename))
Set oFile = oFS.CreateTextFile(sAbsFilename, false, false)

Now, a new data block is created by calling GenGargabeBlock using the default block size (see Declarations). The second parameter allows the function to write output to the console. Then error handling is turned on. This is necessary because there may be problems when the disk is almost full and only a few kilobytes of free space remains. In this situation, an attempt to write data using the TextStream object may fail. Therefore, an error handler is needed. Furthermore, the single byte block allows writing as much data to disk as possible (or in other words, help to delay the occurrence of the exception).

VBScript
sBlock = GenGarbageBlock(lStartBlockSize, true)
On Error Resume Next

A loop is used to continuously write the junk block into the file. The stop criterion is met when there is no free disk space available anymore. However, there are two exceptions. When there is still disk space left but it is smaller than our block of junk, the function switches to single byte block mode. It will then create a new one byte string with every cycle and write it to disk. As described before, an exception may occur when writing, although there is still disk space left. In this case, the Err object is used to determine if an exception occurred. If there was an exception, a message is displayed and the loop is terminated.

VBScript
Do While oDrive.FreeSpace > 0
  If oDrive.FreeSpace < lStartBlockSize Then
    If bSngByteBlock = false Then
      WScript.Echo "INFO: Falling back to single byte block"
      bSngByteBlock = true
    End If
    sBlock = GenGarbageBlock(1, false)
  End If
  oFile.Write sBlock
  If Err.Number <> 0 Then
    WScript.Echo "WARNING: Error " & Chr(34) & Err.Description & Chr(34) & " ("_
      & Err.Number & ") occured while writing garbage file"
    Exit Do
  End If
Loop

The last task is to clean up. That means deactivating error handling and closing our beloved garbage file.

VBScript
On Error GoTo 0
oFile.Close

Function GenGarbageBlock(lSize, bShowMsg)

GenGarbageBlock is responsible for creating a string with random information. Of course, the function Rnd only returns pseudo random numbers. In fact, it uses an algorithm that returns something that looks random, but if you always use the same initial value, you will always get the same numerical series.

VBScript
Dim lCounter

If bShowMsg = True Then
  WScript.Echo "Creating new random data block (" & lSize & " bytes)..."
End If

GenGarbageBlock = ""
Randomize
For lCounter = 1 To lSize
  GenGarbageBlock = GenGarbageBlock & Chr(Int ((255 + 1) * Rnd))
Next

If bShowMsg = True Then
  WScript.Echo "Data block complete"
  WScript.Echo "Continue writing garbage file..."
End If

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Germany Germany
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralCheck out Sysinternals sdelete utility Pin
Russell Cannon19-May-09 8:55
Russell Cannon19-May-09 8:55 
GeneralUsing script to zero free space Pin
Mark Brandon20-Sep-04 3:16
Mark Brandon20-Sep-04 3:16 
GeneralUsing script to zero free space Pin
Dennis C. Dietrich20-Sep-04 15:02
Dennis C. Dietrich20-Sep-04 15:02 
General&quot;free space&quot; in disk images Pin
Dennis C. Dietrich20-Sep-04 15:34
Dennis C. Dietrich20-Sep-04 15:34 
GeneralRe: Using script to zero free space Pin
Dennis C. Dietrich13-Oct-04 14:11
Dennis C. Dietrich13-Oct-04 14:11 
GeneralRe: Using script to zero free space Pin
Mark Brandon14-Oct-04 4:37
Mark Brandon14-Oct-04 4:37 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.