U盘监控
windows应用程序是由消息(事件)驱动的,任何一个窗体都能接收消息,并可以通过回调函数处理消息。同样,U盘和其它移动设备的插入或者拔出也会有消息对应,这个消息是WM_DEVICECHANGE
。若想编程实现对U盘插入拔出的监控,只需要捕获这个消息并对之进行处理就行了。
相关API
WindowProc函数
该函数是一个应用程序定义的函数。它处理发送给窗口的消息。WNDPROC
类型定义了一个指向该回调函数的指针。WindowProc
是用于应用程序定义函数的占位符。而此处专门用来处理WM_DEVICECHANGE
消息。
原型:
LRESULT CALLBACK WindowProc (HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM IParam);
参数:
- hwnd:指向窗口的句柄
- uMsg:指定消息类型,这里是
WM_DEVICECHANGE
消息 - wParam:指定其余的、消息特定的信息。该参数的内容与UMsg参数值有关。此处可以是
Dbt.h
头文件中的以下值之一(列举了几个相关的)。
#define DBT_DEVICEARRIVAL 0x8000 // system detected a new device
#define DBT_DEVICEQUERYREMOVE 0x8001 // wants to remove, may fail
#define DBT_DEVICEQUERYREMOVEFAILED 0x8002 // removal aborted
#define DBT_DEVICEREMOVEPENDING 0x8003 // about to remove, still avail.
#define DBT_DEVICEREMOVECOMPLETE 0x8004 // device is gone
#define DBT_DEVICETYPESPECIFIC 0x8005 // type specific event
- IParam:指定其余的、消息特定的信息。该参数的内容与uMsg参数值有关,格式取决于
wParam
。
DEV_BROADCAST_HDR结构体
struct _DEV_BROADCAST_HDR { /* */
DWORD dbch_size;
DWORD dbch_devicetype;
DWORD dbch_reserved;
};
typedef struct _DEV_BROADCAST_HDR DEV_BROADCAST_HDR;
typedef DEV_BROADCAST_HDR DBTFAR *PDEV_BROADCAST_HDR;
成员变量:
- dbch_size:结构大小(以字节为单位)
- dbch_devicetype:指定设备类型
#define DBT_DEVTYP_OEM 0x00000000 // oem定义的设备类型
#define DBT_DEVTYP_DEVNODE 0x00000001 // devnode number
#define DBT_DEVTYP_VOLUME 0x00000002 // 逻辑卷
#define DBT_DEVTYP_PORT 0x00000003 // 端口设备
#define DBT_DEVTYP_NET 0x00000004 // 网络资源
#define DBT_DEVTYP_DEVICEINTERFACE 0x00000005 // 设备类
#define DBT_DEVTYP_HANDLE 0x00000006 // 文件系统句柄
- dbch_reserved:保留
DEV_BROADCAST_VOLUME结构体
该结构体比DEV_BROADCAST_HDR就多了2个成员变量dbcv_unitmask
和dbcv_flags
struct _DEV_BROADCAST_VOLUME { /* */
DWORD dbcv_size;
DWORD dbcv_devicetype;
DWORD dbcv_reserved;
DWORD dbcv_unitmask;
WORD dbcv_flags;
};
typedef struct _DEV_BROADCAST_VOLUME DEV_BROADCAST_VOLUME;
typedef DEV_BROADCAST_VOLUME DBTFAR *PDEV_BROADCAST_VOLUME;
成员变量:
- dbcv_size:结构体大小
- dbcv_devicetype:设备类型(这里和
DEV_BROADCAST_HDR
用到的一样,此处设为2) - dbcv_reserved:保留
- dbcv_unitmask:标识一个或多个逻辑设备掩码,掩码中的每位对应一个逻辑驱动器。
- dbcv_flags:此参数可以是以下值之一
值 | 含义 |
---|---|
DBTF_MEDIA | 更改影响驱动器的介质。未设置。则更改将影响物理设备或驱动器 |
DBTF_NET | 指示逻辑卷是一个网络卷 |
实现原理
程序主要是对设备的插入和拔出进行监控,所以只要
- 对
WM_DEVICECHANGE
消息回调函数的参数wParam
进行判断,判断它是否为DBT_DEVICEARRIVAL
(设备已插入)以及DBT_DEVICEREMOVECOMPLETE
(设备已移除)操作即可。 - 然后再重点分析相应操作对应的
lParam
消息参数里存储的信息数据,从而产生设备盘符消息。
设备已插入操作DBT_DEVICEARRIVAL
此时参数wParam
的值是DBT_DEVICEARRIVAL
,表示设备已插入操作,接下来便是分析lParam
,lParam
表示事件特定数据结构体的指针,此处是DEV_BROADCAST_HDR
结构体指针。而想要获取设备盘符,则需要以下步骤:
- 判断
lParam
dbch_devicetype
成员变量的值是否是DBT_DEVTYP_VOLUME
(表示该设备是逻辑卷),不是逻辑卷则不会产生盘符。 - 若是逻辑卷,则
lParam
实际上是DEV_BROADCAST_VOLUME
结构体指针(结构体指针是可以强制类型转换的,参考C语言结构体的强制类型转换),其多出的成员变量dbcv_unitmask
可以用来计算盘符。
设备已插入操作DBT_DEVICEARRIVAL
此时参数wParam
的值是DBT_DEVICEARRIVAL
,表示设备已移除操作。接下来就是获取盘符,该过程和设备插入的过程类似。
示例代码
#include<stdio.h>
#include<Windows.h>
#include<Dbt.h>
#define IDD_DIALOG1 101
LRESULT OnDeviceChange(WPARAM wParam, LPARAM lParam)
{
switch (wParam)
{
// 设备已经插入
case DBT_DEVICEARRIVAL:
{
PDEV_BROADCAST_HDR lpdb = (PDEV_BROADCAST_HDR)lParam;
// 逻辑卷
if (DBT_DEVTYP_VOLUME == lpdb->dbch_devicetype)
{
// 根据 dbcv_unitmask 计算出设备盘符
PDEV_BROADCAST_VOLUME lpdbv = (PDEV_BROADCAST_VOLUME)lpdb;
DWORD dwDriverMask = lpdbv->dbcv_unitmask;
DWORD dwTemp = 1;
char szDriver[4] = "A:\\";
for (szDriver[0] = 'A'; szDriver[0] <= 'Z'; szDriver[0]++)
{
if (0 < (dwTemp & dwDriverMask))
{
// 获取设备盘符
::MessageBox(NULL, szDriver, "设备已插入", MB_OK);
}
// 左移1位, 接着判断下一个盘符
dwTemp = (dwTemp << 1);
}
}
break;
}
// 设备已经移除
case DBT_DEVICEREMOVECOMPLETE:
{
PDEV_BROADCAST_HDR lpdb = (PDEV_BROADCAST_HDR)lParam;
// 逻辑卷
if (DBT_DEVTYP_VOLUME == lpdb->dbch_devicetype)
{
// 根据 dbcv_unitmask 计算出设备盘符
PDEV_BROADCAST_VOLUME lpdbv = (PDEV_BROADCAST_VOLUME)lpdb;
DWORD dwDriverMask = lpdbv->dbcv_unitmask;
DWORD dwTemp = 1;
char szDriver[4] = "A:\\";
for (szDriver[0] = 'A'; szDriver[0] <= 'Z'; szDriver[0]++)
{
if (0 < (dwTemp & dwDriverMask))
{
// 获取设备盘符
::MessageBox(NULL, szDriver, "设备已移除", MB_OK);
}
// 左移1位, 接着判断下一个盘符
dwTemp = (dwTemp << 1);
}
}
break;
}
default:
break;
}
return 0;
}
BOOL CALLBACK ProgMainDlg(HWND hWnd, UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
if (WM_DEVICECHANGE == uiMsg)
{
OnDeviceChange(wParam, lParam);
}
else if (WM_CLOSE == uiMsg)
{
::EndDialog(hWnd, NULL);
}
return FALSE;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevinstance,LPSTR lpCmdLine,int nCmdShow)
{
::DialogBoxParam(hInstance, (LPCSTR)IDD_DIALOG1, NULL, (DLGPROC)ProgMainDlg, NULL);
::ExitProcess(NULL);
return 0;
}
执行结果:
插入U盘
拔出U盘
总结
示例程序会监控U盘的插入操作并可以获取加载盘符的根目录,然后用MessageBox
显示出来。实际中的恶意代码还可以加上文件夹遍历和文件读取等操作,从根目录中扫描U盘里的文件,进而读取感兴趣的文件内容,甚至是新建一个连接发回。
参考
Windows黑客编程技术详解