编写 USB 存储设备使用痕迹检测和删除工具

(C# Windows Form 编程练习)

[版权所有 邱秋 2014 metaphysis@yeah.net, 转载请注明出处]

第一节 准备知识

之前一直都是用 Visual Basic .Net 来写 Windows Form 程序。这几天,熟悉了一下 C# 语言的语法,想练习一下。以前使用过一些 USB 存储设备使用痕迹检测和删除工具,于是想写了一个小工具来模拟这些功能。

USB 存储设备在使用后会在注册表留下一些记录,一般是通过检索相应的注册表键值来检查使用痕迹。这些键值包括:

HKEY_LOCAL_MACHINE\SYSTEM\ControlSetXXX(CurrentControSetXXX)\Enum\USB
HKEY_LOCAL_MACHINE\SYSTEM\ControlSetXXX(CurrentControSetXXX)\Enum\USBSTOR
HKEY_LOCAL_MACHINE\SYSTEM\ControlSetXXX(CurrentControSetXXX)\Control\DeviceClasses\{53f56307-b6bf-11d0-94f2-00a0c91efb8b}
HKEY_LOCAL_MACHINE\SYSTEM\ControlSetXXX(CurrentControSetXXX)\Control\DeviceClasses\{a5dcbf10-6530-11d2-901f-00c04fb951ed}

其中 ControlSetXXX 和 CurrentControlSetXXX 表示的是注册表中的类似于 ControlSet001、ControlSet002、CurrentControlSet 这样的子键(CurrentControlSet 子键一般只有一个,特殊情况下可能有 CurrentControlSet001 等多个,同样的 ControlSet 一般只有 ControlSet001 和 ControlSet002 这两个,特殊情况下可能会有多个),CurrentControlSet 保存的是系统的当前的一些配置信息,而 ControlSet001 等则是对当前配置信息的备份,一般注册表都会有两个以上的备份,有的时候可能会有更多。在 ControlSetXXX 中的信息和 CurrentControlSet 中的信息一般都是一样的,所以在检测和删除 USB 存储设备信息时,不仅要检测 CurrentControlSet 子键,也要检测 ControlSetXXX 子键。

对于 Enum\USB 子键,其中存储的是曾经连接到系统的 USB 设备的一些信息,包括 USB 鼠标、键盘、光驱、手机、移动硬盘、摄像头、U 盘等,所以并不是所有的信息都是 USB 存储设备信息,虽然把这些信息删除并无大碍(因为删除的只是这些设备的一些连接信息和相应的驱动信息,并不是删除系统的实际驱动文件,所以系统会自动重新识别这些设备),但为了提高识别精确性,还是增加一些判断来得好些。

而对于 Enum\USBSTOR 子键来说,则相当于把 USB 存储设备的信息单独分离出来了,该子键下存储的信息都是曾经连接到计算机的 USB 存储设备的相关信息。这些信息详细列出了该 USB 存储设备的类型、硬件 ID、设备描述、友好名称等信息。

对于 Control\DeviceClasses 来说,该子键下存储的是以 GUID 分类的设备信息,其中有几个是和 USB 设备有关的(它们在微软的 USB 和存储设备输入输出控制头文件 USBIODEF.H 和NTDDSTOR.H 中定义):

{A5DCBF10-6530-11D2-901F-00C04FB951ED} GUID_DEVINTERFACE_USB_DEVICE
{3ABF6F2D-71C4-462A-8A92-1E6861E6AF27} GUID_DEVINTERFACE_USB_HOST_CONTROLLER
{F18A0E88-C30C-11D0-8815-00A0C906BED8} GUID_DEVINTERFACE_USB_HUB
{53F56307-B6BF-11D0-94F2-00A0C91EFB8B} GUID_DEVINTERFACE_DISK

一般检查 {53F56307-B6BF-11D0-94F2-00A0C91EFB8B} 和 {A5DCBF10-6530-11D2-901F-00C04FB951ED} 这两个键值,其他两个由于是和 USB 控制器有关,一般可不检查。有的资料介绍说还要检查 MountedDevices 子键,但是由于该子键和系统的分区信息有关,一般无绝对的把握,不必进行检查和删除,到时可能会造成不必要的麻烦。

第二节 检测 USB 存储设备使用痕迹

1、打开 Visual Studio 2010,新建一个空白方案,命名为 USBView。

2、在解决方案下新建一个类型为“Windows 窗体应用程序”的项目,命名为 USBViewer。将主窗体命名为 MainForm。并添加一些图标和 PNG 图像资源,美化一下程序界面。

资源文件包含了一个文本文件,名称为 VendorIDs.txt,保存的是 USB 设备厂家的 ID 号及厂家名称值对,用于显示在注册表中搜索到的 USB 设备的厂家名称(该文件内容来源于微软的 USBView 示例程序中的头文件vndrlist.h,该示例程序使用 C++ 编码,需要使用 Visual Studio 2013打开,并要求安装 Windows Driver Kit (WDK)8.1,下载链接地址 http://code.msdn.microsoft.com/windowshardware/USBView-sample-application-e3241039)。

在建立必要的按钮后,开始编写检测代码。因为要读取注册表,有必要使用 .Net 框架提供的 RegistryKey 类。这需要引用 Microsoft.Win32 命名空间,注册表类都在该命名空间中。

using Microsoft.Win32;

之后便是获取我们感兴趣的注册表的基项 HKEY_LOCAL_MACHINE,接着使用RegistryKey 类的 OpenSubKey 方法以只读方式打开 SYSTEM 子键。

RegistryKey hklm = Registry.LocalMachine;
RegistryKey systemKey = hklm.OpenSubKey("SYSTEM");

这里顺带说一下,.Net 框架已经将注册表中的数据封装成了 Registry 类(注意 Registry 类和 RegistryKey 类是不一样的),并公布了几个只读属性,它们是:

RegistryKey 类是用于在注册表中检索数据用的类。以下是本文中用到的 RegistryKey 类的几个属性和方法:

SubKeyCount                     // 检索当前项的子项数目。
OpenSubKey(string)              // 以只读方式检索 string 标识的子项。
OpenSubKey(string, bool)        // 以 bool 指定的方式检索 string 标识的子项,若 bool 值为 true,表示以可读写方式打开,否则为只读方式。
DeleteSubKeyTree(string)        // 递归删除 string 标识的子项和任何子级子项。
GetValue(string)                // 检索 string 标识的名称所关联的值,若该标识的名称不存在,则返回 null。
GetSubKeyNames()                // 返回一个包含所有子项名称的字符串数组。
Close()                         // 关闭该键,如果有更改,则将更改刷新到磁盘上。

需要注意的是若想用可写方式打开某个子项进行删除或写入操作,一定要具有相应的权限,否则会抛出 SecurityException 异常。在检测阶段,由于只需要只读权限,可以使用方法 OpenSubKey(string)。在打开了 Local_Machine\SYSTEM 子键后,开始检索该键下类似于 ControlSetXXX 和 CurrentControlSetXXX 的键。

if (!sysKey.ToUpper().StartsWith("CONTROLSET") &&!sysKey.ToUpper().StartsWith("CURRENTCONTROLSET"))continue;

找到这样的子键后,就在其中检索 Enum\USB、Enum\USBSTOR、Control\DeviceClasses 等子键。由于 Enum\USB 子键中的信息并不都是 USB 存储设备的信息,所以要加以判断。观察一下 Enum\USB 子键下的项:

除了 ROOT_HUB20 外,其他键值都是以 VID_ 开始,后面跟着一个 4 位的16 进制数,这就是厂商的 ID 号,其后的 PID_XXXX 则是厂家的产品号。进一步打开这些 VID_ 打头的子键键值,可以看到更多的信息:

例如 Service 表示的是该 USB 设备是由系统服务 HidUSB 进行处理的,而系统服务 HidUSB 表示的是使用 USB 协议的人体工程学输入设备,只要有 USB 类的鼠标、键盘、摄像头等设备连接到计算机,就会触发一个特定于 HID 设备的事件,HidUSB 服务会处理该事件,可以看到有 ClassGUID 这个键值,它的值 {745a17a0-74d3-11d0-b6fe-00a0c90f57da} 就是表示 Class 是 HIDClass 这样的设备。同样的,对于 USB 存储设备,在接入时同样会由一个服务予以处理,这个服务就是 USBSTOR 系统服务。以下列出了在 Enum\USB 下和 USB 设备有关的服务名称(还有其他的相关服务,这里列出的是我的系统上有的):

Usbccgp:       USB 设备控制器服务。
HidUsb:        USB 人体学工程输入设备服务。
USBSTOR:       USB 存储设备服务。
Usbhub:        USB 设备接口服务。
WUDFRd:        用户模式驱动框架反射器相关的服务,当连接只能手机等设备时,该服务会进行处理。
VboxUsb:       安装 Oracle 的 VM VirtualBox 虚拟机软件后会出现该服务,用于处理连接到虚拟机的 USB 设备。

在本文中,只检测了 USBSTOR、WUDFRd、VboxUsb服务所处理的 USB 设备。对于 Enum\USBSTOR 子键的键值,其结构和 Enum\USB 下的键值结构类似。在 USB、USBSTOR 的子键键值中有FriendlyName 这个键值,它表示的是设备的友好名称,可以作为 USB 的设备名称来显示。

第三节 删除 USB 存储设备使用痕迹

检测到之后便是删除这些痕迹的问题。由于注册表的某些键值权限是 SYSTEM 账户的,一般的管理员账号权限无法删除,如果要想通过代码来删除,必须取得 SYSTEM 权限,一个方法是将执行删除的代码做成程序后安装为系统服务(Sysinterals 的工具 PsExec 也是利用了系统服务可以使用 SYSTEM 权限的特性,使得启动的进程继承了 SYSTEM 权限),系统服务是可以运行于 SYSTEM 权限的,这样对注册表就有了完全的控制权,可自由进行删改(但是同时一定要注意不要删除其他的注册表项,以避免麻烦)。如何使用 Visual Studio 制作系统服务呢,Step by Step。

1、右键单击解决方案 USBView,选择“添加”-“新建项目”,项目类型选择“Windows 服务”,项目名称命名为 USBCleaner,随后在 IDE 自动生成的代码中会出现以下方法:

protected override void OnStart(string[] args)
protected override void OnStop()

分别表示服务启动和停止时的事件处理过程。在这两个事件处理过程中写入删除注册表键值的代码,逻辑和检测时的一样。在删除过程中,我添加了一个日志功能,记录在执行删除过程中成功和错误的信息。为了在程序运行目录下生成日志文件,需要在系统服务的运行过程中得到其所在路径,网上查阅了一些资料,有许多获取路径的方法,自己实际用注册服务的方式做了一下实验,结果如下:

Assembly.GetExecutingAssembly().Location
D:\DATA\USBViewer\bin\Release\USBCleaner.exethis.GetType( ).Assembly.Location
D:\DATA\USBViewer\bin\Release\USBCleaner.exeProcess.GetCurrentProcess().MainModule.FileName
D:\DATA\USBViewer\bin\Release\USBCleaner.exeSystem.Environment.CurrentDirectory
C:\Windows\system32System.AppDomain.CurrentDomain.BaseDirectory
D:\DATA\USBViewer\bin\Release\System.AppDomain.CurrentDomain.SetupInformation.ApplicationBase
D:\DATA\USBViewer\bin\Release\System.IO.Directory.GetCurrentDirectory()
C:\Windows\system32

最后决定用 Assembly.GetExecutingAssembly().Location 这个方法,在 Windows 7 和 Windows XP 及 Windows Server 2003 上运行都能得到正确的结果。

在所有删除代码都编写完毕后,就是给服务添加安装程序了。双击打开 USBCleaner.cs,此时会显示设计界面,在设计界面上单击右键,选择“添加安装程序”,会为该服务项目添加用于服务安装时的支持,这样就可以通过代码来直接安装和卸载服务了。

“添加安装程序”这一步会在项目中自动添加两个组件,一个是服务进程安装类组件,另外一个是服务安装类组件:

服务进程安装类组件具有以下的属性:

其中的“Account”属性即是服务所运行的权限级别,选择“LocalSystem”,即可以使用 SYSTEM 权限。另外一个是服务安装类组件,属性如下:

其中的几个关键属性:

Description  用于该服务的简短描述。
DisplayName 显示在服务列表中的显示名称。
ServiceName 服务的名称,系统使用该名称唯一标识该服务,不能和已有的服务冲突。
StartType   表示服务的启动方式,可以是自动、手动、禁用。

设置完毕后,生成 USBCleaner,得到一个 EXE 文件:

接下来就是在项目 USBViewer 中对 USBCleaner.exe 进行安装调用了。

如果是手动进行安装,可以使用 .Net 框架提供的工具:InstallUtil.exe 程序,对于 .Net Framework 4.0 来说,它一般位于:

SystemRoot%\Microsoft.NET\Framework\v4.0.30319\InstallUtil.exe

使用 /u 或 /uninstall 来卸载服务,不加参数则是安装服务。但是要在程序中自动执行安装和卸载服务,就需要使用 .Net 框架提供的 TransactedInstaller 类,它提供了的作用就是InstallUtil.exe 服务安装工具所提供的功能。

为了代码更好的组织,我将检测服务运行和安装的代码统一放在 USBViewer 项目的 ServiceHelper.cs 文件中。代码首先添加对以下两个命名空间的引用:

using System.Configuration.Install;
using System.ServiceProcess;

引用 System.Configuration.Install 的目的是使用 TransactedInstaller 类,该类的作用是以事务的方式对服务进行安装或卸载,要么安装或卸载成功,要么回到安装前的状态。在进行操作前常规对服务进行一下检测,看服务是否已经存在,这里使用了 ServiceController 类对服务名进行轮询,只要查找到具有指定名称的服务,就可以知道服务已经存在了。

/// 
/// 检测服务是否安装。 ///
/// 要查询的服务名。
/// private static bool IsWindowsServiceInstalled(string serviceName)
{ServiceController[] services = ServiceController.GetServices();foreach (ServiceController service in services){if (service.ServiceName == serviceName)return true;}return false;
}

安装服务则按以下方式调用 TransactedInstaller 类:

string[] cmdline = {};
TransactedInstaller transactedInstaller = new TransactedInstaller();
AssemblyInstaller assemblyInstaller = new AssemblyInstaller(serviceFileName, cmdline);
transactedInstaller.Installers.Add(assemblyInstaller);
transactedInstaller.Install(new System.Collections.Hashtable());

注意 Install 方法需要一个 IDictionary 接口类型的变量来保存安装状态,这里使用了哈希表。卸载服务和安装服务时类似,不过是调用 Uninstall 方法,而且可以不用提供保存状态的变量。

transactedInstaller.Uninstall(null);

在安装和卸载前,不要忘记对要安装和卸载的服务文件进行检测,确保该文件确实存在。

// 检测相应的服务文件是否存在。
string serviceFileName = Path.Combine(Application.StartupPath, "USBCleaner.exe");
if (File.Exists(serviceFileName) == false)return false;

第四节 运行测试

对项目 USBViewer 进行编译,得到一个 EXE 文件:

因为在删除过程中要安装系统服务,运行本程序,需要系统管理员权限,这在 Windows XP 和 Windows Server 2003 等操作系统上,需要以管理员账户登录系统后使用。在 Windows Vista 或 Windows 7 以上的操作系统,由于 UAC (用户账户控制)的问题,可能程序无法运行,毕竟Windows Vista 以上的系统对系统安全性做了进一步增加,对注册表等敏感数据的操作做了进一步的限制,要使得程序能够正常运行,一个方法可以通过右键单击程序,在“兼容性”选项卡,选择“以管理员身份运行本程序”。

或者在每次运行程序时,单击右键选择“以管理员身份运行”。

另外一种方法是使用 app.manifest 文件的方式,把程序运行所需要的权限事先声明,这样就可以避免 Windows UAC 的控制阻拦问题了。具体方法是:在项目 USBViewer 右键单击,选择“属性”-“安全性”,勾选“启用 ClickOnce 安全设置”,

这时会自动为项目添加一个 app.manifest 文件(在项目的 Property 文件夹下),打开该文件,找到

将其中的 asInvoker 替换为 requireAdministrator,

保存编译项目。注意在保存编译项目前,注意要取消勾选项目安全性中的“启用 ClickOnce 安全设置”,否则编译会出错(取消勾选并不会删除已经生成的 app.manifest 文件)。这样程序就会自动以管理员权限运行了。

解决了权限的问题后,将删除 USB 存储设备使用痕迹的服务程序 USBCleaner.exe 复制到 USBViewer.exe 所在文件夹下,运行 USBViewer.exe,结果如下:

执行删除操作,可以看到系统服务列表里出现了显示名称为 USB Cleaner 的服务:

查看其属性,确实是执行注册表删除操作的服务。

点击“退出”按钮,稍等程序对服务进行卸载,然后再次查看一下系统服务列表,发现 USB Cleaner 服务被卸载了。检查 USBViewer.exe 文件所在文件夹,会发现多了两个文件,一个是 USBCleanerLog.txt,它是执行删除操作的日志文件:

而另外一个文件 USBCleaner.InstallLog 则是在执行安装和卸载服务时产生的日志文件,可打开查看其中的内容。

第五节 小结

通过此次 C# WindowsForm编程练习,进一步熟练了C#的使用,对注册表的操作和 Windows服务的安装及卸载有了进一步的了解,同时对相关的知识做了一番梳理,有利于自己编程水平的提高(本文所涉及的解决方案文件已压缩为 RAR格式的压缩文件以供下载,下载链接:http://download.csdn.net/detail/metaphysis/6864213)。

用 C# 编写 USB 存储设备使用痕迹检测和删除工具相关推荐

  1. 树莓派linux系统识别u盘启动,树莓派Raspberry Pi实战之命令行下实现USB存储设备自动挂载...

    简单介绍实现命令行下USB存储设备自动挂载的方法,Linux gnome/kde窗口环境下有移动存储的管理程序,可以实现自动挂载移动存储设备,但是在命令行下 通常需要用mount命令手动挂载USB存储 ...

  2. 开始计算机USB存储功能,USB存储设备禁用怎么设置

    因为有些情况下我们必须得禁止USB存储设备,特别是涉及到当文件被泄漏或者复制时,那如何设置禁用USB存储设备呢?下面是学习啦小编为大家介绍禁用USB存储设备的设置方法,欢迎大家阅读. 禁用USB存储设 ...

  3. 将windows系统装到USB存储设备

    将windows系统装到USB存储设备 需求: 1)一般公司比较规范,计算机系统有严格的限制策略,如果自己不懂得如何更改或者没有权限更改,将极其不便. 2)计划在家里完成在公司未完成的事,甚至异地出差 ...

  4. 你的计算机usb管理策略,您的计算机已经实施了USB存储设备管理策略,系统发现你使用了USB存储设备,该设备已被阻止,如有疑问请与...

    满意答案 eq8276 2014.04.21 采纳率:42%    等级:9 已帮助:663人 要禁用 USB 存储设备,请根据您的具体情况使用下面的一个或多个步骤: 如果计算机上尚未安装 USB 存 ...

  5. linux 自动挂载usb设备,Raspberry Pi 自动挂载USB存储设备

    简单介绍实现命令行下USB存储设备自动挂载的方法,Linux gnome/kde窗口环境下有移动存储的管理程序,可以实现自动挂载移动存储设备,但是在命令行下 通常需要用mount命令手动挂载USB存储 ...

  6. 计算机怎样发现路由器上u盘,360 P2 无线路由器通过电脑终端查看路由USB存储设备操作流程 路由器...

    首先,将您的U盘 或 插入存储卡的读卡器 或 移动硬盘(以下称为USB存储设备),插入5G路由后面的USB接口上,如下图 插入USB存储设备后,5G路由面板上的USB指示灯(左数第三个)和后面USB卸 ...

  7. PS4 不支持USB存储设备的文件系统 如何解决?

    昨日我在拷贝PS4上通过SHARP录制的星际战甲影像在插入我 的U盘时,突然看到屏幕左上角出现提示说"不支持USB存储设 备的文件系统"的提示,但是以前我插这张U盘时PS4都可以识 ...

  8. 树莓派USB存储设备自动挂载并通过脚本实现自动拷贝,自动播放视频,脚本自动升级等功能...

    需求: 首先需要树莓派自动挂载USB设备,然后扫描USB指定目录下文件,将相关文件拷贝至树莓派指定目录,然后通过omxplayer循环播放新拷贝文件视频 1. 树莓派实现USB存储设备自动挂载 树莓派 ...

  9. 掀开USB存储设备自动运行的面纱(精简版)

    前前言2:为什么有精简版? 考虑原文(http://blog.sina.com.cn/u/56b798f8010007ht)太长而且比较罗嗦,部分新手会失去耐心看完,因此特地修改原文推出精简版.精简版 ...

最新文章

  1. ubuntu安装KVM虚拟机管理virt-manager
  2. idea未进行快捷键配置时的快捷键
  3. java调用C++ DLL库方法
  4. Java EE 7是最终版本。 思想,见解和进一步的指针。
  5. mac eclipse tomcat mysql_mac下使用eclipse+tomcat+mysql开发 j2ee(一)
  6. NFS部署及优化(一)
  7. 程序固化到优盘中_将Windows 8/10 系统装进优盘
  8. 演讲实录:MySQL 8.0 中的复制技术
  9. 2021年中国成人商店市场趋势报告、技术动态创新及2027年市场预测
  10. python模块下载1002python模块下载_【Python】Python的urllib模、urllib2模块的网络下载文件...
  11. 少撸两局教你搭个博客玩
  12. Go语言详细介绍:logo和版本
  13. 电脑软件上的按钮原来是这样来的:按钮组件
  14. 获取客户端访问的ip地址
  15. linux access函数报错,详解汇编语言MOV指令
  16. DB9 公头母头引脚定义及连接
  17. 支付宝小程序生成二维码
  18. FPGA 视频处理 FIFO 的典型应用
  19. top与with ties用法
  20. 【红外遥控器】基于FPGA的学习型红外遥控器verilog开发

热门文章

  1. 最简单的分类算法之一:KNN(原理解析+代码实现)
  2. 网页调用腾讯qq在线客服
  3. 百度云加速zblog和WordPress规则自定义
  4. idea中使用git查看每一行代码是谁写的
  5. 台式电脑计算机用不了,台式电脑关不了机怎么办
  6. Dashboard是什么意思 Dashboard怎么用?
  7. python安装方法3.8.2_Python 3.8.2详细图文安装教程(附安装包) | 我爱分享网
  8. 计算机 无法进入pe,U盘重装系统|无法进入韩博士PE系统怎么办
  9. 【this,super关键字使用】经典习题
  10. 打工与创业的有什么区别,丨国仁网络资讯