65.9K
CodeProject 正在变化。 阅读更多。
Home

擦除 NTFS 分区上的空闲空间数据

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.13/5 (5投票s)

2004年9月5日

8分钟阅读

viewsIcon

63108

downloadIcon

691

一个VBScript脚本,通过创建一个包含随机数据且(大部分)与空闲空间大小相同的文件,覆盖NTFS分区上的空闲磁盘空间,以防止文件恢复。

引言

本周我订购了一台新电脑,以替换我家里的一台私人机器。由于我仍在犹豫如何处理旧电脑,而出售它是一个选项,所以我认为擦除两个硬盘上的所有旧数据可能是一个好主意。问题是格式化并不能真正做到这一点,留下以后可能恢复文件的可能性。所以,我写了这个小脚本,你不会想在情报部门使用它(你稍后会明白为什么),但它应该足以用于家庭使用。因为它是一个命令行脚本,所以必须通过CScript运行。

CScript Overwrite.vbs [path] [filename]

全局声明

基本思想是创建一个文件,该文件占用分区上的所有空闲空间并仅包含随机数据。因此,我的脚本以一些默认设置开始,然后调用Main

Option Explicit

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

Call Main

Option Explicit 强制您明确声明要使用的变量。这有助于避免因打字错误而意外创建的变量,而这些错误可能非常难以调试。常量包含默认目录、默认文件名和块大小。目录和文件名应该不言自明。块大小定义了每次调用TextStream.WriteCreateGarbageFile中写入的字节数。在尝试了一些块大小之后,我决定使用一个32千字节的块(= 32 * 1024字节 = 215字节),因为一个更大的块在200MHz处理器上创建会花费太多时间,而一个32Kb的块即使在2GHz处理器上创建也需要几秒钟。

Sub Main()

这个Sub Main完成了所有关于满足先决条件的工作,并且还以一些局部声明和初始化开始。

变量`sPath`和`sFilename`将存储实际值,这些值最终可以是默认值,也可以是作为命令行参数传递的值。其他变量将用于各种对象。`ShowStartMsg`是一个简单的方法,它只是使用`WScript.Echo`来显示一个静态启动消息。

最后两行更有趣。`WScript`对象的`Arguments`属性返回命令行参数的集合。`FileSystemObject`对象是所有文件I/O操作所必需的,例如处理路径、处理驱动器或创建文件。该对象必须在使用前通过调用`CreateObject`来创建,`CreateObject`也是`WScript`的一个方法。

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

  ShowStartMsg

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

我执行的第一个检查是,是否只有一个命令行参数,并且该参数等于`/?`。我通过调用`Count`来获取`WshArguments`集合中的元素数量,并通过使用它们的索引(从0开始)访问元素,因此用法与其他任何集合都没有区别。如果两个条件都为真,则调用`ShowHelp`以显示有关如何使用脚本的一些信息。参数值`true`告诉`ShowHelp`通过调用`WScript.Quit`来取消脚本的执行。

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

然后我检查命令行是否传递了两个以上的参数。因为脚本只支持两个参数(都是可选的),所以这将是一个无效的输入。Sub ShowMsg被定义为Sub ShowMsg(sMsg, bShowHelpHint, bExit),其中`sMsg`是一个要在控制台上显示的字符串。`bShowHelpHint`决定是否显示如何启动*Overwrite.vbs*以获取帮助的提示,而`bExit`决定在显示消息后脚本是否应该退出。

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

之后,我检查用户是否提供了路径。如果提供了,我将调用`GetAbsolutePathName`以供进一步使用。`GetAbsolutePathName`将相对路径名转换为绝对路径名。这对于使脚本工作并不重要,但当告诉用户正在做什么时,始终使用绝对路径名可以使脚本的输出明确无误。如果未指定路径,我将`sPath`初始化为空字符串。

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

下一步是验证给定路径是否存在。`FolderExists`为我做了这件事。我所需要做的就是传递我想要验证的路径,在本例中是`sPath`的内容。如果用户省略了反斜杠,则会在用户指定的路径后面附加一个反斜杠。这允许通过组合路径和文件名(sPath & sFilename)来获取我们要写入的文件的有效路径。

  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

知道路径没问题后,就需要验证文件名。这分三步完成。第一步是分析命令行参数。如果用户指定了文件名且不为空,则将此文件名复制到`sFilename`中(用户提供的空文件名被视为错误条件)。如果未指定文件名,则将默认文件名(参见声明)复制到`sFilename`中。

  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

其次,我们需要确保文件名不包含任何无效字符。这通过使用正则表达式来完成。在创建`RegExp`类的一个实例后,我将`Pattern`属性设置为我的正则表达式,其中包含所有禁止的字符。然后,我调用`Execute`并将`sFilename`作为参数传递。返回值为`Matches`集合。因为我只想知道我的文件名是否包含任何无效字符,所以我不关心检查集合的内容,只关心它包含多少个元素。如果它根本不包含任何元素,则文件名是有效的。

  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

因为用户可能会输入现有文件的名称,而我真的不想删除用户数据或程序文件,所以我最终通过调用`FileSystemObject``FileExists`方法来确保指定的文件不存在。

  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

现在,我确保我的目标是 NTFS 分区。为什么呢?因为我想编写一个简单的脚本,它只写入一个大文件。使用 FAT 文件系统意味着最大文件大小为 4 GB(如果是 FAT32)。但是,空闲空间可能大于此,我不想创建多个文件(实际上我甚至不再使用 FAT 文件系统)以使脚本尽可能简单。

为了获取这些信息,我们需要一个`Drive`对象。要获取它,我们只需调用`GetDrive`并将其参数设置为`GetDriveName`的返回值(传递整个路径会导致异常,如果它不指向根目录)。然后我们只需检查`FileSystem`属性是否设置为NTFS。

  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

我们最后要防止的是有人通过网络写入随机数据。因此,我们使用`Drive`对象的`DriveType`属性来获取驱动器类型,并且只有当它是固定驱动器或可移动驱动器时才继续。

  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

最后一步检查最终验证是否有任何允许我们写入的空闲空间。如果配额处于活动状态,这不一定是分区的空闲空间。此信息由`FreeSpace`属性返回。

  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

现在我们知道一切就绪,我们可以开始向驱动器填充垃圾数据了。Main只向控制台写入一些输出,调用CreateGarbageFile,并通过调用DeleteFile删除垃圾文件。

  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`需要两个参数。`sAbsFilename`是包含完整路径的文件名,`oFS`是一个`FileSystemObject`对象。当函数进入单字节块模式时,局部变量`bSngByteBlock`被设置为`true`,而`sBlock`是写入磁盘的字符串。`Drive`对象在讨论Main时介绍过,`oFile`是一个`TextStream`对象。此对象用于实际将数据写入磁盘,并通过调用`FileSystemObject`类的`CreateTextFile`方法创建。该调用的第一个参数是文件名。第二个参数告诉函数不要覆盖文件,第三个参数表示将使用8位ASCII(替代方案是Unicode)。

  Dim bSngByteBlock
  Dim sBlock
  Dim oFile, oDrive

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

现在,通过使用默认块大小(参见声明)调用GenGargabeBlock来创建新的数据块。第二个参数允许函数将输出写入控制台。然后打开错误处理。这是必要的,因为当磁盘几乎已满且只剩下几千字节的空闲空间时,可能会出现问题。在这种情况下,尝试使用TextStream对象写入数据可能会失败。因此,需要一个错误处理程序。此外,单字节块允许尽可能多地将数据写入磁盘(或者换句话说,有助于延迟异常的发生)。

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

循环用于不断将垃圾块写入文件。当没有可用的空闲磁盘空间时,满足停止条件。但是,有两种例外情况。当仍然有磁盘空间但它小于我们的垃圾块时,函数会切换到单字节块模式。然后,它将在每个循环中创建一个新的单字节字符串并将其写入磁盘。如前所述,即使仍然有磁盘空间,在写入时也可能发生异常。在这种情况下,`Err`对象用于确定是否发生了异常。如果发生了异常,则显示一条消息并终止循环。

  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

最后的任务是清理。这意味着停用错误处理并关闭我们心爱的垃圾文件。

  On Error GoTo 0
  oFile.Close

Function GenGarbageBlock(lSize, bShowMsg)

`GenGarbageBlock`负责创建一个包含随机信息的字符串。当然,函数`Rnd`只返回伪随机数。实际上,它使用一种算法,返回看起来随机的东西,但如果你总是使用相同的初始值,你将总是得到相同的数值序列。

  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
© . All rights reserved.