HidHide 驱动分析 - HidHideClient 篇(中):白名单对话框(WhitelistDlg)
一、白名单对话框的功能定位
CWhitelistDlg 是主对话框中的应用标签页,负责管理白名单(Whitelist) ------即允许访问隐藏设备的应用程序列表。用户可以通过"插入"按钮选择可执行文件,或直接通过拖放(Drag & Drop)将 .exe、.com、.bin 文件拖入列表,从而实现快速添加。该对话框同时提供"删除"按钮移除选中条目,以及"反转白名单"复选框用于切换逻辑模式。
二、类结构与成员变量
cpp
class CWhitelistDlg : public CDialogEx, public HidHide::IDropTarget {
CHidHideClientDlg& m_HidHideClientDlg; // 父对话框引用
HidHide::FullImageNames m_DropTargetFullImageNames; // 拖放时缓存的路径列表
CListBox m_Whitelist; // 显示白名单的列表框
CStatic m_Guidance; // 提示标签
CButton m_Insert; // 插入按钮
CButton m_Delete; // 删除按钮
CButton m_Inverse; // 反转复选框
// 方法:OnDragEnter, OnDragOver, OnDropEx, OnBnClicked... 等
};
HidHide::FullImageNames 是 std::set<std::filesystem::path> 的类型别名,确保白名单中无重复条目。
三、初始化与资源加载(OnInitDialog)
cpp
m_Guidance.SetWindowTextW(HidHide::StringTable(IDS_STATIC_WHITELIST_GUIDANCE).c_str());
m_Insert.SetWindowTextW(HidHide::StringTable(IDS_BUTTON_WHITELIST_INSERT).c_str());
m_Delete.SetWindowTextW(HidHide::StringTable(IDS_BUTTON_WHITELIST_DELETE).c_str());
m_Inverse.SetWindowTextW(HidHide::StringTable(IDS_CHECK_WHITELIST_INVERSE).c_str());
m_Inverse.SetCheck(FilterDriverProxy().GetInverse() ? BST_CHECKED : BST_UNCHECKED);
所有标签从字符串表资源加载,支持本地化。反转复选框的初始状态从驱动读取,保持与内核一致。
四、列表内容的刷新(Refresh 与 OnUserMessageRefresh)
由于设备或进程状态可能动态变化(如白名单被外部程序修改),Refresh 方法通过 PostMessage(WM_USER_CM_NOTIFICATION_REFRESH) 异步刷新 UI,避免在敏感的窗口消息处理中阻塞。
OnUserMessageRefresh 执行实际刷新逻辑:
cpp
m_Whitelist.ResetContent();
m_Whitelist.SetSel(-1, FALSE);
for (auto const& fullImageName : FilterDriverProxy().GetWhitelist()) {
auto const index = m_Whitelist.AddString(fullImageName.c_str());
// 检查文件是否存在,若不存在则高亮(选中)提示用户
auto const exists = std::filesystem::exists(HidHide::FullImageNameToFileName(fullImageName));
if (!exists) m_Whitelist.SetSel(index, TRUE);
}
m_Whitelist.SetFocus();
高亮不存在的文件条目,帮助用户识别已失效的应用路径,及时清理。
五、插入操作(OnBnClickedButtonWhitelistInsert)
- 清除当前列表框的选择状态。
- 使用
CFileDialog打开文件选择对话框,过滤条件为可执行文件(.exe, .com, .bin)。 - 用户选择文件后,通过
HidHide::FileNameToFullImageName将逻辑盘符路径转换为 NT 设备路径(如\Device\HarddiskVolume1\...),这是驱动白名单所需的格式。 - 遍历现有列表,防止重复添加。
- 调用
m_Whitelist.AddString添加到列表框,并通过FilterDriverProxy().SetWhitelist(ListBoxToPathSet(m_Whitelist))同步到驱动。
关键函数 FileNameToFullImageName 的实现原理 :通过 GetVolumeNameForVolumeMountPoint 和 QueryDosDeviceW 建立逻辑盘符到设备名称的映射,拼接出完整设备路径。这确保了即使盘符在重启后改变,白名单依然能精准匹配。
六、删除操作(OnBnClickedButtonWhitelistDelete)
- 调用
m_Whitelist.GetSelCount()获取选中的条目数量,并通过GetSelItems获取所有选中索引。 - 从后往前遍历索引,逐个调用
DeleteString删除(从后往前避免索引变化影响)。 - 更新后的列表通过
SetWhitelist同步到驱动。
七、拖放支持(IDropTarget 接口实现)
7.1 OnDragEnter:准入判断
当用户拖拽文件进入窗口时,OnDragEnter 首先提取拖拽的文件列表(通过 HidHide::DragTargetFileNames(pDataObject)),然后遍历检查:
- 文件扩展名是否为
.exe、.com或.bin。 - 文件是否能成功转换为全路径(
FileNameToFullImageName非空)。
若全部满足,则将转换后的路径存入 m_DropTargetFullImageNames,并返回 DROPEFFECT_COPY;否则返回 DROPEFFECT_NONE,阻止拖放。
7.2 OnDragOver:实时反馈
在拖拽过程中,OnDragOver 检查鼠标位置是否位于列表框区域内(MousePointerAtWhitelist),同时确认按键状态符合复制操作(左键 + 无 Ctrl/Shift/Alt)。只有满足条件时才显示"可放置"光标,提供清晰的交互反馈。
7.3 OnDropEx:执行放置
当用户释放鼠标时,OnDropEx 执行最终添加逻辑:
cpp
auto dirty = false;
auto whitelist = FilterDriverProxy().GetWhitelist();
for (auto const& fullImageName : m_DropTargetFullImageNames) {
if (whitelist.emplace(fullImageName).second) dirty = true;
}
if (dirty) {
FilterDriverProxy().SetWhitelist(whitelist);
Refresh();
}
return DROPEFFECT_COPY;
利用 std::set::emplace 的返回值判断是否新增了条目,避免不必要的驱动写入。
八、反转复选框(OnBnClickedCheckInverse)
cpp
FilterDriverProxy().SetInverse(0 != (m_Inverse.GetCheck() & BST_CHECKED));
立即将反转状态同步到驱动,无需重启。
九、错误处理与异常安全
所有 UI 操作均被 try-catch 包裹(在消息响应函数中由 MFC 顶层捕获,但代码中显式使用 THROW_WIN32 抛出),异常信息通过全局异常处理机制向用户展示。列表框操作(如 AddString)若返回 LB_ERR 或 LB_ERRSPACE,会抛出异常,避免静默失败。