0%

应用层删除USB存储设备历史记录(1)

简介

当我们把一个U盘插入到电脑的USB接口后,操作系统会读取U盘相关信息,并在注册表中生成对应注册表项,
而我们拔掉这个U盘后,相关注册表项并不会自动删除,操作系统会保留对应信息,用以再次接入时使用。

某些安全检测软件,可以检测到这些历史记录信息。有时候,我们并不想让这些安全软件检测到,这就需要
我们主动的清除这些信息。

手动操作

首先我们需要在 环境变量 中新建一个 DEVMGR_SHOW_NONPRESENT_DEVICES 项,并设置变量值为 1

环境变量

然后打开 设备管理器 ,并在 查看 菜单里勾选上 显示隐藏的设备 的选项

设备管理器

磁盘驱动器通用串行总线控制器 两处可以看到 半透明图标 的历史设备记录

磁盘驱动器

通用串行总线控制器

半透明图标 的设备上,单击鼠标右键选择 卸载 就会删除该条设备记录

卸载设备

编程操作

写代码实现手动卸载操作,需要用 SetupDiRemoveDevice 函数来实现

1
2
3
4
BOOL SetupDiRemoveDevice(
_In_ HDEVINFO DeviceInfoSet,
_Inout_ PSP_DEVINFO_DATA DeviceInfoData
);

其他相关函数都在 setupapi.h 头文件中定义,所以需要加载该头文件

1
2
#include <setupapi.h>
#pragma comment(lib, "setupapi.lib")

方法为:遍历所有 USB 设备,检查设备使用的服务是否是 USBSTOR,是的话就删除。
对应到手动操作:在 设备管理器 中卸载 通用串行总线控制器 中的 USB 大容量存储设备 项。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
BOOL DeleteUsbHistory()
{
// 删除USB设备相关记录
HDEVINFO hDevInfo = SetupDiGetClassDevs(NULL, "USB", NULL, DIGCF_ALLCLASSES);
if (hDevInfo == INVALID_HANDLE_VALUE) return FALSE;
// 根据设备类信息句柄枚举设备信息
DWORD dwDevIndex = 0;
char szBuffer[40] = { 0 };
SP_DEVINFO_DATA DevInfoData = { 0 };
do
{
// 查询设备信息
ZeroMemory(&DevInfoData, sizeof(SP_DEVINFO_DATA));
DevInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
if (!SetupDiEnumDeviceInfo(hDevInfo, dwDevIndex, &DevInfoData)) break;
dwDevIndex++;
// 获取服务信息
ZeroMemory(szBuffer, sizeof(szBuffer));
if (!SetupDiGetDeviceRegistryProperty(hDevInfo, &DevInfoData,
SPDRP_SERVICE, NULL, (PBYTE)szBuffer, sizeof(szBuffer), NULL)) continue;
// 判断是不是USBSTOR和UASPStor
if ((_stricmp(szBuffer, "USBSTOR") != 0) &&
(_stricmp(szBuffer, "UASPStor") != 0)) continue;
// 卸载该设备
SetupDiRemoveDevice(hDevInfo, &DevInfoData);
} while (1);
// 获取的设备列表要手动销毁
SetupDiDestroyDeviceInfoList(hDevInfo);
return TRUE;
}

删除 磁盘驱动器 中的USB相关存储设备,需要遍历所有 USBSTOR 设备,直接删除

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
BOOL DeleteUsbStorHistory()
{
// 删除USBSTOR设备相关记录
HDEVINFO hDevInfo = SetupDiGetClassDevs(NULL, "USBSTOR", NULL, DIGCF_ALLCLASSES);
if (hDevInfo == INVALID_HANDLE_VALUE) return FALSE;
// 根据设备类信息句柄枚举设备信息
DWORD dwDevIndex = 0;
char szBuffer[40] = { 0 };
SP_DEVINFO_DATA DevInfoData = { 0 };
do
{
// 查询设备信息
ZeroMemory(&DevInfoData, sizeof(SP_DEVINFO_DATA));
DevInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
if (!SetupDiEnumDeviceInfo(hDevInfo, dwDevIndex, &DevInfoData)) break;
dwDevIndex++;
// 卸载该设备
SetupDiRemoveDevice(hDevInfo, &DevInfoData);
} while (1);
// 获取的设备列表要手动销毁
SetupDiDestroyDeviceInfoList(hDevInfo);
return TRUE;
}

从Win8开始,操作系统提供了一种新的UASP协议。使用该协议时,对应的服务就变为 UASPStor
该协议没有专门的设备类,需要在 SCSI 类中处理,然后再查询对应的父设备是否是 UASPStor 服务

获取父设备以及查询对应服务,相关函数在 cfgmgr32.h 中定义,但是使用 VS2013 时,选择兼容XP系统
编译,就会提示找不到 cfgmgr32.lib,所以就需要我们从WDK7.1中手动拷贝对应XP系统的库到工程中

1
2
#include <cfgmgr32.h>
#pragma comment(lib, "cfgmgr32.lib")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
BOOL DeleteUaspStorHistory()
{
// 删除UASPSTOR设备相关记录
HDEVINFO hDevInfo = SetupDiGetClassDevs(NULL, "SCSI", NULL, DIGCF_ALLCLASSES);
if (hDevInfo == INVALID_HANDLE_VALUE) return FALSE;
// 根据设备类信息句柄枚举设备信息
DWORD dwDevIndex = 0;
char szBuffer[40] = { 0 };
SP_DEVINFO_DATA DevInfoData = { 0 };
DEVINST DevInstParent = 0;
ULONG ulLength = 0;
do
{
// 查询设备信息
ZeroMemory(&DevInfoData, sizeof(SP_DEVINFO_DATA));
DevInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
if (!SetupDiEnumDeviceInfo(hDevInfo, dwDevIndex, &DevInfoData)) break;
dwDevIndex++;
// 获取父设备实例
DevInstParent = 0;
if (CM_Get_Parent(&DevInstParent, DevInfoData.DevInst, 0) != CR_SUCCESS) continue;
// 获取服务信息
ulLength = sizeof(szBuffer);
if (CM_Get_DevNode_Registry_Property(DevInstParent,
CM_DRP_SERVICE, NULL, szBuffer, &ulLength, 0) != CR_SUCCESS) continue;;
// 判断是不是UASPStor
if ((_stricmp(szBuffer, "UASPStor") != 0)) continue;
// 卸载该设备
SetupDiRemoveDevice(hDevInfo, &DevInfoData);
} while (1);
// 获取的设备列表要手动销毁
SetupDiDestroyDeviceInfoList(hDevInfo);
return TRUE;
}