涉密文件处理方案二:原地打包

实践表明,吉印测绘地理信息数据安全检查系统不仅仅通过CAD的文件名来筛查,它还会扫描CAD中的所有图层,如果图层名称涉密也会被筛查出来。不仅如此,除了dwg文件,还会检查tif文件,通过其内容判断tif是否属于涉密数据。那么之前的“通过文件名与文件大小”来筛查的思路就行不通了,而编写一个能解析dwg、tif内容的程序显然不太现实。

于是想到采用压缩文件。经测试,安全检查系统不会检测压缩包内的文件,原因是显而易见的:

1、全盘的压缩文件都解压缩一遍,费时费力。

2、压缩文件很容易加上密码,那么检查系统就无法查看里面的具体内容。

总体思路与关键逻辑

基本思路:

对本地分区的所有dwg、tif逐个进行压缩操作,并存放在原始位置。那么需要用到winrar的命令行工具rar.exe,它是winrar自带的。

由于所有的文件都在原地,应还能方便地针对某一个文件夹及其子文件夹进行单独操作,可以考虑把它加入到文件夹的右键菜单。

关键安全逻辑:

1、脚本应跳过用户映射的网络驱动器,只处理真正的本地分区文件。

2、需要考虑到潜在的文件权限问题,仅当压缩/解压缩成功时,才删除原始文件/原始压缩文件。

3、由于涉及到多个后缀名,用户可能对处理的后缀名进行扩展,应对敏感后缀名进行校验,避免处理exe、dll等会破坏系统的后缀。

4、应输出完整的log日志文件。

操作友好性:

1、用注册表添加文件夹的右键菜单,并指定使用winrar.exe中包含的现成图标。

2、将针对某个文件夹操作的脚本copy到system32目录,实现在任意位置的便捷执行。

工具实现

准备winrar

首先提供winrar安装程序,默认应安装在c:\program files\winrar。

环境配置文件:setup.vbs

setup.vbs负责6件事情:1)自动提权,因为注册表操作、复制文件到system32都需要管理员权限;2)检测winrar是否存在;3)将rar.exe所在目录写入系统环境变量;4)初始化powershell的执行策略;5)复制必要的文件到c:\windows\system32;6)导入右键菜单注册表。

Option Explicit

' 初始化对象
Dim fso, shell, ws
Set fso = CreateObject("Scripting.FileSystemObject")
Set shell = CreateObject("Shell.Application")
Set ws = CreateObject("WScript.Shell")

' 自动提权检测(用RegWrite法,最稳)
On Error Resume Next
ws.RegWrite "HKLM\SOFTWARE\TestAdminKey", "1", "REG_SZ"
If Err.Number <> 0 Then
    shell.ShellExecute "wscript.exe", """" & WScript.ScriptFullName & """ uac", "", "runas", 1
    WScript.Quit
End If
ws.RegDelete "HKLM\SOFTWARE\TestAdminKey"
On Error GoTo 0

' 基本路径和日志初始化
Dim sys32, regfile, scriptPath, logPath, logfile
sys32 = ws.ExpandEnvironmentStrings("%SystemRoot%\System32")
scriptPath = fso.GetParentFolderName(WScript.ScriptFullName)
logPath = scriptPath & "\setup_log.txt"
Set logfile = fso.OpenTextFile(logPath, 8, True)

Sub Log(msg)
    logfile.WriteLine Now & " " & msg
End Sub

'---1. 检查WinRAR是否安装在 C:\Program Files\WinRAR\ ---
Dim winrar
winrar = "C:\Program Files\WinRAR\WinRAR.exe"
If Not fso.FileExists(winrar) Then
    MsgBox "未检测到WinRAR,或WinRAR未安装在C:\Program Files\WinRAR\!" & vbCrLf & "请手动安装WinRAR到默认路径。", 48, "WinRAR未安装"
    Log "WinRAR未安装或位置不符,脚本终止"
    logfile.Close
    WScript.Quit
End If
Log "检测到WinRAR,路径:" & winrar

'---2. 把 WinRAR 加入系统环境变量 Path ---
Dim pathEnv, pathKey, newPath, pathAlreadySet
Dim envTarget
envTarget = "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment\Path"
pathAlreadySet = False

On Error Resume Next
pathEnv = ws.RegRead(envTarget)
If Err.Number <> 0 Then
    MsgBox "无法读取系统环境变量 Path,请确保以管理员权限运行!", 16, "权限不足"
    Log "无法读取系统环境变量 Path,脚本终止"
    logfile.Close
    WScript.Quit
End If
On Error GoTo 0

If InStr(1, LCase(pathEnv), LCase("C:\Program Files\WinRAR"), vbTextCompare) > 0 Then
    pathAlreadySet = True
    Log "WinRAR 路径已存在于 Path,无需添加"
Else
    If Right(pathEnv, 1) <> ";" Then
        newPath = pathEnv & ";C:\Program Files\WinRAR"
    Else
        newPath = pathEnv & "C:\Program Files\WinRAR"
    End If
    On Error Resume Next
    ws.RegWrite envTarget, newPath, "REG_EXPAND_SZ"
    If Err.Number = 0 Then
        Log "已将 WinRAR 路径加入系统环境变量 Path"
    Else
        Log "写入 Path 环境变量失败,请手动添加"
        MsgBox "写入 Path 环境变量失败,请手动添加:C:\Program Files\WinRAR", 16, "写入失败"
        logfile.Close
        WScript.Quit
    End If
    On Error GoTo 0
End If

'---3. 设置执行策略---
Dim cmd, exec
cmd = "powershell -NoProfile -Command ""Set-ExecutionPolicy RemoteSigned -Scope CurrentUser -Force"""
Set exec = ws.Exec(cmd)
Do While exec.Status = 0
    WScript.Sleep 200
Loop
Log "已设置执行策略 RemoteSigned"

'---4. 复制四个文件到System32,来源于setup.vbs当前目录的system32子目录---
Dim filelist, file, src, dst, srcDir
srcDir = scriptPath & "\system32"
filelist = Array("CompressCur.ps1", "DecompressCur.ps1", "jieyasuo.bat", "yasuo.bat")
For Each file In filelist
    src = srcDir & "\" & file
    dst = sys32 & "\" & file
    If Not fso.FileExists(src) Then
        MsgBox "缺少文件:" & src & vbCrLf & "请确保system32子文件夹中包含全部脚本!", 16, "缺少文件"
        Log "缺少文件:" & src
        logfile.Close
        WScript.Quit
    End If
    fso.CopyFile src, dst, True
    Log "已复制:" & src & " 到 " & dst
Next

'---5. 导入注册表文件,文件名为“右键注册表.reg”---
regfile = scriptPath & "\右键注册表.reg"
If Not fso.FileExists(regfile) Then
    MsgBox "缺少注册表文件:右键注册表.reg" & vbCrLf & "请放在setup.vbs同一目录下!", 16, "缺少文件"
    Log "缺少注册表文件"
    logfile.Close
    WScript.Quit
End If

cmd = "reg import """ & regfile & """"
Set exec = ws.Exec(cmd)
Do While exec.Status = 0
    WScript.Sleep 200
Loop
Log "已导入注册表:" & regfile

MsgBox "全部准备工作已自动完成!日志文件见:" & logPath, 64, "安装完成"
logfile.Close

全盘检索并压缩备份脚本:CompressALL.ps1

它负责校验处理的后缀名,检索全部本地分区(跳过网络映射分区),调用rar.exe压缩,文件名加上特殊的后缀以与正常压缩文件区分。随后检测压缩成功后删除原文件。全部过程写入日志文件,日志位于c:\logs。ps1脚本使用UTF-8带BOM编码,以取得最高的兼容性。

$suffixes = @('dwg','tif')
$forbidden = @(
    'exe','dll','com','bat','cmd','msi','vbs','js','ps1','vbe','wsh','wsf','sh','inf',
    'rar','zip','7z','tar','gz','cab',
    'sys','drv','ocx','scr',
    'ini','cfg','conf','reg',
    'db','mdb','ldb','sqlite','sqlite3',
    'tmp','bak','log','lnk',
    'asp','aspx','php','jsp','html','htm'
)

$forbiddenMatch = $suffixes | Where-Object { $forbidden -contains $_.ToLower() }
if ($forbiddenMatch) {
    Write-Host "后缀配置包含敏感/高危后缀:$($forbiddenMatch -join ', '),程序终止执行。" -ForegroundColor Red
    exit 2
}
$drives = Get-PSDrive -PSProvider FileSystem | Where-Object { $_.DisplayRoot -notlike '\\*' }
$logDir = "C:\logs"
if (-not (Test-Path $logDir)) {
    New-Item -Path $logDir -ItemType Directory | Out-Null
}
$timestamp = Get-Date -Format "yyyyMMdd_HHmmss"
$log = Join-Path $logDir "CompressALL_$timestamp.log"

foreach ($drive in $drives) {
    Write-Host "正在扫描分区: $($drive.Root)" -ForegroundColor Yellow
    Get-ChildItem -Path $drive.Root -Recurse -File -ErrorAction SilentlyContinue | Where-Object {
        $ext = $_.Extension.TrimStart('.').ToLower()
        $suffixes -contains $ext
    } | ForEach-Object {
        $file = $_
        $suffix = $file.Extension.TrimStart('.').ToLower()
        $rarName = "$($file.DirectoryName)\$($file.BaseName)-Compressed-$suffix-ByScript.rar"
        Write-Host "正在压缩: $($file.FullName)" -ForegroundColor Cyan

        $winrar = "C:\Program Files\WinRAR\rar.exe"
        $res = & $winrar a -ep1 "$rarName" "$($file.FullName)" | Out-Null

        if ((Test-Path -LiteralPath $rarName) -and ((Get-Item -LiteralPath $rarName).Length -gt 0)) {
            Remove-Item -LiteralPath $file.FullName -Force
            $msg = "$(Get-Date) 成功压缩并删除: $($file.FullName) -> $rarName"
            Write-Host $msg -ForegroundColor Green
            Add-Content -Path $log -Value $msg
        } else {
            $msg = "$(Get-Date) 压缩失败: $($file.FullName)"
            Write-Host $msg -ForegroundColor Red
            Add-Content -Path $log -Value $msg
        }
    }
}
Write-Host "全部压缩任务完成。日志文件位于:$log" -ForegroundColor Yellow

全盘检索并解压缩恢复脚本:DecompressAll.ps1

它负责检索全部本地分区的特殊压缩文件,调用rar.exe解压缩,检测解压缩成功后删除压缩包。全部过程写入日志文件,日志位于c:\logs。ps1脚本使用UTF-8带BOM编码,以取得最高的兼容性。

$suffixes = @('dwg', 'tif')
$forbidden = @(
    'exe','dll','com','bat','cmd','msi','vbs','js','ps1','vbe','wsh','wsf','sh','inf',
    'rar','zip','7z','tar','gz','cab',
    'sys','drv','ocx','scr',
    'ini','cfg','conf','reg',
    'db','mdb','ldb','sqlite','sqlite3',
    'tmp','bak','log','lnk',
    'asp','aspx','php','jsp','html','htm'
)

$forbiddenMatch = $suffixes | Where-Object { $forbidden -contains $_.ToLower() }
if ($forbiddenMatch) {
    Write-Host "后缀配置包含敏感/高危后缀:$($forbiddenMatch -join ', '),程序终止执行。" -ForegroundColor Red
    exit 2
}
$drives = Get-PSDrive -PSProvider FileSystem | Where-Object { $_.DisplayRoot -notlike '\\*' }
$logDir = "C:\logs"
if (-not (Test-Path $logDir)) {
    New-Item -Path $logDir -ItemType Directory | Out-Null
}
$timestamp = Get-Date -Format "yyyyMMdd_HHmmss"
$log = Join-Path $logDir "DecompressALL_$timestamp.log"

foreach ($drive in $drives) {
    Write-Host "正在扫描分区: $($drive.Root)" -ForegroundColor Yellow
    # 全量递归,不用任何筛选参数,防止漏查
    Get-ChildItem -Path $drive.Root -Recurse -File -ErrorAction SilentlyContinue | Where-Object {
        $file = $_.Name
        foreach ($suffix in $suffixes) {
            if ($file -like "*-Compressed-$suffix-ByScript.rar") { return $true }
        }
        return $false
    } | ForEach-Object {
        $file = $_
        $suffix = $suffixes | Where-Object { $file.Name -like "*-Compressed-$_-ByScript.rar" }
        if (-not $suffix) { return }
        # 这里用BaseName更安全
        $pattern = "-Compressed-$suffix-ByScript$"
        $origBase = $file.BaseName -replace $pattern, ''
        $origFile = Join-Path $file.DirectoryName "$origBase.$suffix"

        Write-Host "正在解压: $($file.FullName)" -ForegroundColor Cyan
        $winrar = "C:\Program Files\WinRAR\rar.exe"
        $res = & $winrar x -o+ "$($file.FullName)" "$($file.DirectoryName)" | Out-Null

        if ((Test-Path -LiteralPath $origFile) -and ((Get-Item -LiteralPath $origFile).Length -gt 0)) {
            Remove-Item -LiteralPath $file.FullName -Force
            $msg = "$(Get-Date) 解压成功并删除压缩包: $($file.FullName) -> $origFile"
            Write-Host $msg -ForegroundColor Green
            Add-Content -Path $log -Value $msg
        } else {
            $msg = "$(Get-Date) 解压失败: $($file.FullName)"
            Write-Host $msg -ForegroundColor Red
            Add-Content -Path $log -Value $msg
        }
    }
}
Write-Host "全部解压任务完成。日志文件位于:$log" -ForegroundColor Yellow

两个用于执行ps1脚本的bat

为了方便用户直接鼠标点击即可执行ps1,增加两个bat,分别用来执行上述两个ps1。注意,bat需要保存为ANSI格式。

1全盘压缩dwg和tif.bat

@echo off
setlocal enabledelayedexpansion

echo.
echo 即将开始用rar处理潜在的涉密文件。
pause

rem 当前目录下的 ps1
set "script_dir=%~dp0"
set "ps1_path=%script_dir%CompressAll.ps1"

rem 以管理员启动 PowerShell ;-NoExit 保持窗口, -Wait 让 bat 等待
powershell -NoProfile -ExecutionPolicy Bypass -Command ^
   "Start-Process powershell -Verb RunAs -Wait -ArgumentList '-NoProfile','-ExecutionPolicy','Bypass','-NoExit','-File','\"%ps1_path%\"'"

endlocal

2全盘解压缩dwg和tif.bat,内容基本相同,只需要修改一下ps1的文件名称即可。

单个文件夹处理的ps1脚本

首先是CompressCur.ps1,用于压缩。接受一个参数,用来传递“当前工作目录”。

param(
    [string]$targetDir = (Get-Location).Path
)

if (-not (Test-Path $targetDir)) {
    Write-Host "目录不存在:$targetDir" -ForegroundColor Red
    exit 1
}

# 检查是否为网络映射分区
$driveLetter = $targetDir.Substring(0,2)
$isNet = Get-PSDrive -PSProvider FileSystem | Where-Object { $_.Name + ':' -eq $driveLetter -and $_.DisplayRoot -like '\\*' }
if ($isNet) {
    Write-Host "当前目录是网络映射盘,终止执行。" -ForegroundColor Yellow
    exit 0
}

$suffixes = @('dwg','tif')
$forbidden = @(
    'exe','dll','com','bat','cmd','msi','vbs','js','ps1','vbe','wsh','wsf','sh','inf',
    'rar','zip','7z','tar','gz','cab',
    'sys','drv','ocx','scr',
    'ini','cfg','conf','reg',
    'db','mdb','ldb','sqlite','sqlite3',
    'tmp','bak','log','lnk',
    'asp','aspx','php','jsp','html','htm'
)

$forbiddenMatch = $suffixes | Where-Object { $forbidden -contains $_.ToLower() }
if ($forbiddenMatch) {
    Write-Host "后缀配置包含敏感/高危后缀:$($forbiddenMatch -join ', '),程序终止执行。" -ForegroundColor Red
    exit 2
}
$logDir = "C:\logs"
if (-not (Test-Path $logDir)) {
    New-Item -Path $logDir -ItemType Directory | Out-Null
}
$timestamp = Get-Date -Format "yyyyMMdd_HHmmss"
$log = Join-Path $logDir "CompressCur_$timestamp.log"

Write-Host "正在扫描当前目录及子目录: $targetDir" -ForegroundColor Yellow
Get-ChildItem -Path $targetDir -Recurse -File -ErrorAction SilentlyContinue | Where-Object {
    $ext = $_.Extension.TrimStart('.').ToLower()
    $suffixes -contains $ext
} | ForEach-Object {
    $file = $_
    $suffix = $file.Extension.TrimStart('.').ToLower()
    $rarName = "$($file.DirectoryName)\$($file.BaseName)-Compressed-$suffix-ByScript.rar"
    Write-Host "正在压缩: $($file.FullName)" -ForegroundColor Cyan

    $winrar = "C:\Program Files\WinRAR\rar.exe"
    $res = & $winrar a -ep1 "$rarName" "$($file.FullName)" | Out-Null

    if ((Test-Path -LiteralPath $rarName) -and ((Get-Item -LiteralPath $rarName).Length -gt 0)) {
        Remove-Item -LiteralPath $file.FullName -Force
        $msg = "$(Get-Date) 成功压缩并删除: $($file.FullName) -> $rarName"
        Write-Host $msg -ForegroundColor Green
        Add-Content -Path $log -Value $msg
    } else {
        $msg = "$(Get-Date) 压缩失败: $($file.FullName)"
        Write-Host $msg -ForegroundColor Red
        Add-Content -Path $log -Value $msg
    }
}
Write-Host "全部压缩任务完成。日志文件位于:$log" -ForegroundColor Yellow

然后是DecompressCur.ps1,用来解压缩,同样接受一个参数,用来传递“当前工作目录”。

param(
    [string]$targetDir = (Get-Location).Path
)

if (-not (Test-Path $targetDir)) {
    Write-Host "目录不存在:$targetDir" -ForegroundColor Red
    exit 1
}

# 检查是否为网络映射分区
$driveLetter = $targetDir.Substring(0,2)
$isNet = Get-PSDrive -PSProvider FileSystem | Where-Object { $_.Name + ':' -eq $driveLetter -and $_.DisplayRoot -like '\\*' }
if ($isNet) {
    Write-Host "当前目录是网络映射盘,终止执行。" -ForegroundColor Yellow
    exit 0
}

$suffixes = @('dwg','tif')
$forbidden = @(
    'exe','dll','com','bat','cmd','msi','vbs','js','ps1','vbe','wsh','wsf','sh','inf',
    'rar','zip','7z','tar','gz','cab',
    'sys','drv','ocx','scr',
    'ini','cfg','conf','reg',
    'db','mdb','ldb','sqlite','sqlite3',
    'tmp','bak','log','lnk',
    'asp','aspx','php','jsp','html','htm'
)

$forbiddenMatch = $suffixes | Where-Object { $forbidden -contains $_.ToLower() }
if ($forbiddenMatch) {
    Write-Host "后缀配置包含敏感/高危后缀:$($forbiddenMatch -join ', '),程序终止执行。" -ForegroundColor Red
    exit 2
}
$logDir = "C:\logs"
if (-not (Test-Path $logDir)) {
    New-Item -Path $logDir -ItemType Directory | Out-Null
}
$timestamp = Get-Date -Format "yyyyMMdd_HHmmss"
$log = Join-Path $logDir "DecompressCur_$timestamp.log"

Write-Host "正在扫描当前目录及子目录: $targetDir" -ForegroundColor Yellow
Get-ChildItem -Path $targetDir -Recurse -File -ErrorAction SilentlyContinue | Where-Object {
    $file = $_.BaseName
    foreach ($suffix in $suffixes) {
        if ($file -like "*-Compressed-$suffix-ByScript") { return $true }
    }
    return $false
} | ForEach-Object {
    $file = $_
    $suffix = $suffixes | Where-Object { $file.BaseName -like "*-Compressed-$_-ByScript" }
    if (-not $suffix) { return }
    $pattern = "-Compressed-$suffix-ByScript$"
    $origBase = $file.BaseName -replace $pattern, ''
    $origFile = Join-Path $file.DirectoryName "$origBase.$suffix"

    Write-Host "正在解压: $($file.FullName)" -ForegroundColor Cyan
    $winrar = "C:\Program Files\WinRAR\rar.exe"
    $res = & $winrar x -o+ "$($file.FullName)" "$($file.DirectoryName)" | Out-Null

    if ((Test-Path -LiteralPath $origFile) -and ((Get-Item -LiteralPath $origFile).Length -gt 0)) {
        Remove-Item -LiteralPath $file.FullName -Force
        $msg = "$(Get-Date) 解压成功并删除压缩包: $($file.FullName) -> $origFile"
        Write-Host $msg -ForegroundColor Green
        Add-Content -Path $log -Value $msg
    } else {
        $msg = "$(Get-Date) 解压失败: $($file.FullName)"
        Write-Host $msg -ForegroundColor Red
        Add-Content -Path $log -Value $msg
    }
}
Write-Host "全部解压任务完成。日志文件位于:$log" -ForegroundColor Yellow

单个文件夹处理的bat脚本

将上述两个ps1拷贝到system32文件夹后,右键已经可以执行它们了。但为了满足更灵活的使用场景,再追加两个bat文件。

yasuo.bat,顾名思义,“压缩”的拼音。它也接受一个参数,也可以不设参数。不设参数时,target即为当前文件夹。

@echo off
if "%~1"=="" (
  set "target=%cd%"
) else (
  set "target=%~1"
)
powershell -NoProfile -ExecutionPolicy Bypass -File "c:\windows\system32\CompressCur.ps1" "%target%"

jieyasuo.bat,负责执行解压缩ps1。

@echo off
if "%~1"=="" (
  set "target=%cd%"
) else (
  set "target=%~1"
)
powershell -NoProfile -ExecutionPolicy Bypass -File "c:\windows\system32\DecompressCur.ps1" "%target%"

上述4个文件复制到c:\windows\system32,以便在命令行的任意当前目录下直接执行bat,或右键点击文件夹执行对应的ps1脚本。

右键注册表.reg

编辑“右键注册表.reg”,注意使用ANSI编码。在添加两个右键菜单的同时,设置好对应的图标,就用winrar.exe中的现成图标。内容如下:

Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOT\Directory\shell\CompressSecure]
@="压缩潜在涉密文件"
"Icon"="C:\\program files\\winrar\\winrar.exe,4"

[HKEY_CLASSES_ROOT\Directory\shell\CompressSecure\command]
@="powershell -NoExit -NoProfile -WindowStyle Normal -ExecutionPolicy Bypass -File \"c:\\windows\\system32\\CompressCur.ps1\" \"%1\""

[HKEY_CLASSES_ROOT\Directory\shell\DecompressSecure]
@="恢复潜在涉密文件"
"Icon"="C:\\program files\\winrar\\winrar.exe,5"

[HKEY_CLASSES_ROOT\Directory\shell\DecompressSecure\command]
@="powershell -NoExit -NoProfile -WindowStyle Normal -ExecutionPolicy Bypass -File \"c:\\windows\\system32\\DecompressCur.ps1\" \"%1\""

文件准备

假设保存所有相关脚本的文件夹是c:\tools。

为了清爽起见,将以下4个文件放到c:\tools\system32子目录,防止误操作(因为它们不是用来直接双击执行的)。

CompressCur.ps1
DecompressCur.ps1
jieyasuo.bat
yasuo.bat

setup.vbs中已经注明了它们的位置,在执行setup.vbs的时候会自动将它们copy到c:\windows\system32。

使用方法

相关工具下载:点击下载

具体使用方法详见压缩包中的“使用说明.txt”。