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



编写 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表示的是注册表中的类似于ControlSet001ControlSet002CurrentControlSet这样的子键(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.HNTDDSTOR.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-00C04

FB951ED}这两个键值,其他两个由于是和 USB控制器有关,一般可不检查。有的资料介绍说还要检查 MountedDevices子键,但是由于该子键和系统的分区信息有关,一般无绝对的把握,不必进行检查和删除,到时可能会造成不必要的麻烦。

 

 

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

 

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

 

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

 

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

 

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

 

资源文件包含了一个文本文件,名称为VendorIDs.txt,保存的是 USB设备厂家的 ID号及厂家名称值对,用于显示在注册表中搜索到的 USB设备的厂家名称(该文件内容来源于微软的 USBView示例程序中的头文件vndrlist.h,该示例程序使用 C++编码,需要使用 Visual Studio 2013打开,并要求安装 Windows Driver KitWDK8.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类是不一样的),并公布了几个只读属性,它们是:

 

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

 

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\USBEnum\USBSTORControl\DeviceClasses等子键。

由于 Enum\USB子键中的信息并不都是 USB 存储设备的信息,所以要加以判断。观察一下 Enum\USB子键下的项:

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

 

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

 

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

 

例如 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 安装 OracleVMVirtualBox虚拟机软件后会出现该服务,用于处理连接到虚拟机的 USB设备。

 

在本文中,只检测了 USBSTORWUDFRdVboxUsb服务所处理的 USB 设备。对于Enum\USBSTOR子键的键值,其结构和 Enum\USB下的键值结构类似。在USBUSBSTOR的子键键值中有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.exe

 

this.GetType( ).Assembly.Location

D:\DATA\USBViewer\bin\Release\USBCleaner.exe

 

Process.GetCurrentProcess().MainModule.FileName

D:\DATA\USBViewer\bin\Release\USBCleaner.exe

 

System.Environment.CurrentDirectory

C:\Windows\system32

 

System.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,此时会显示设计界面,在设计界面上单击右键,选择“添加安装程序”,会为该服务项目添加用于服务安装时的支持,这样就可以通过代码来直接安装和卸载服务了。

 

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

 

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

 

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

 

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

 

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

 

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

 

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

 

其中的几个关键属性:

 

Description  用于该服务的简短描述。

DisplayName  显示在服务列表中的显示名称。

ServiceName  服务的名称,系统使用该名称唯一标识该服务,不能和已有的服务冲突。

StartType    表示服务的启动方式,可以是自动、手动、禁用。

 

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

 

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

 

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

 

如果是手动进行安装,可以使用 .Net框架提供的工具:InstallUtil.exe程序,对于 .NetFramework 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类对服务名进行轮询,只要查找到具有指定名称的服务,就可以知道服务已经存在了。

 

///<summary>

///检测服务是否安装。

///</summary>

///<paramname="serviceName">要查询的服务名。</param>

///<returns></returns>

private static bool IsWindowsServiceInstalled(string serviceName)

{

Service Controller[] services = ServiceController.GetServices();

foreach (ServiceController service in services)

{

if (service.ServiceName == serviceName)

returntrue;

}

returnfalse;

}

 

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

 

string[] cmdline = {};

TransactedInstaller transactedInstaller = newTransactedInstaller();

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文件:

 

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

 

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

 

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

 

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

 

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

 

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

 

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

 

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

 

<requestedExecutionLevel level="asInvoker" uiAccess="false" />

 

将其中的 asInvoker 替换为 requireAdministrator

 

<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />

 

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

 

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

 

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

 

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

 

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

 

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

 

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

 

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

 

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

 

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

 

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

 

 

第五节 小结

 

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

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

上一篇:UFLDL学习笔记(6)


下一篇:贪心算法——字典序最小问题