一个提供 USB 设备过滤功能的 Linux 内核模块,支持 UEFI 变量持久化。该模块允许您通过厂商 ID (VID) 和产品 ID (PID) 黑名单机制来阻止特定的 USB 设备。
- USB 设备阻止:通过 VID:PID 禁用 USB 设备
- UEFI 持久化:将黑名单存储在 UEFI 变量中,实现重启后持久化
- Sysfs 接口:通过
/sys/kernel/usb_filter/
提供用户友好的接口 - DKMS 支持:内核更新时自动重新构建内核模块
- 跨架构支持:支持 x86_64、ARM64 和 ARM32 架构
- 内核兼容性:支持 Linux 内核 6.6+(已测试至 6.16)
- Linux 内核 6.6 或更高版本
- 支持 EFI 变量的 UEFI 固件
- DKMS(动态内核模块支持)
- GCC 编译器和内核头文件
# 构建 DKMS 包
dpkg-buildpackage -us -uc
# 安装包
sudo dpkg -i ../usb-filter-dkms_0.1.0_all.deb
# 模块将通过 DKMS 自动构建和安装
# 克隆仓库
git clone <repository-url>
cd usb
# 构建模块
make
# 加载模块
sudo insmod usb_filter.ko
# 或带参数加载
sudo insmod usb_filter.ko blacklist="0781:5591,0a12:0001" use_efi=1
模块在 /sys/kernel/usb_filter/
提供 sysfs 接口,包含以下属性:
cat /sys/kernel/usb_filter/list
# 输出:0781:5591,0a12:0001
# 添加单个设备
echo "0781:5591" | sudo tee /sys/kernel/usb_filter/add
# 添加多个设备
echo "0781:5591,0a12:0001,1234:5678" | sudo tee /sys/kernel/usb_filter/add
# 移除单个设备
echo "0781:5591" | sudo tee /sys/kernel/usb_filter/remove
# 移除多个设备
echo "0781:5591,0a12:0001" | sudo tee /sys/kernel/usb_filter/remove
echo "1" | sudo tee /sys/kernel/usb_filter/clear
# 查看当前状态
cat /sys/kernel/usb_filter/enable
# 输出:1(启用)或 0(禁用)
# 禁用过滤
echo "0" | sudo tee /sys/kernel/usb_filter/enable
# 启用过滤
echo "1" | sudo tee /sys/kernel/usb_filter/enable
您也可以使用内核参数配置模块:
# 带初始黑名单加载
sudo insmod usb_filter.ko blacklist="0781:5591,0a12:0001"
# 禁用 UEFI 持久化
sudo insmod usb_filter.ko use_efi=0
可用参数:
blacklist
:逗号分隔的十六进制 VID:PID 条目(例如:"0781:5591,0a12:0001")use_efi
:启用 UEFI 变量持久化(默认:true)
模块分为以下几个组件:
src/
├── main.c # 主模块入口点和 kprobe 处理器
├── hook.c # 设备过滤逻辑
├── include/
│ ├── usb_filter.h # 公共 API 定义
│ └── pt_regs_compat.h # 跨架构兼容性
├── core/
│ ├── list.c # 黑名单管理
│ └── api.c # 导出的 API 包装器
├── storage/
│ └── efi.c # UEFI 变量持久化
├── iface/
│ └── sysfs.c # Sysfs 接口
└── parse/
└── parse.c # 字符串解析工具
- Kprobe 拦截:使用 kprobes 拦截
usb_device_match
调用 - 设备过滤:在设备匹配期间检查 USB 设备是否在黑名单中
- UEFI 持久化:将黑名单存储在 UEFI 变量中实现持久化
- Sysfs 接口:为黑名单管理提供用户空间接口
模块使用 kprobes 拦截 USB 设备匹配函数 usb_device_match ,而不修改内核结构:
// 获取usb_device_match符号
static struct kprobe usb_match_kp = {
.symbol_name = "usb_device_match",
};
usb_match_kp.post_handler = usb_match_post_handler;
// 后置处理器:如果设备应该被阻止则修改返回值
static void usb_match_post_handler(struct kprobe *p, struct pt_regs *regs, unsigned long flags)
{
int original_ret = (int)PT_REGS_RET(regs);
if (usb_filter_enabled && device_filter(current_dev, current_drv)) {
PT_REGS_SET_RET(regs, 0); // 阻止设备
}
}
通过阻止设备和驱动匹配,可实现设备功能无法正常工作,同时设备本身可以用lsusb看到。
流程说明:
- 设备插入:USB设备插入时,USB子系统开始设备枚举过程
- 设备匹配:系统调用
usb_device_match
函数来匹配设备和驱动 - Kprobe拦截:我们的模块通过kprobe拦截这个函数调用
- 原始执行:让原始的
usb_device_match
函数正常执行 - 结果检查:Post-handler检查过滤状态和设备黑名单
- 返回值修改:如果设备在黑名单中,将返回值改为0(匹配失败)
- 最终结果:
- 匹配成功:设备加载驱动,正常工作
- 匹配失败:设备可见但无驱动,功能被禁用
黑名单存储在自定义 UEFI 变量中:
- 变量名:
UsbFilterBlacklist
- GUID:自定义 GUID,避免与系统变量冲突
- 格式:逗号分隔的 VID:PID 对(例如:"0781:5591,0a12:0001")
模块包含不同架构的兼容性头文件:
- x86_64
- ARM64 (AArch64)
- ARM32
- 通用后备方案
# 检查模块是否成功加载
lsmod | grep usb_filter
# 检查内核消息
dmesg | grep usb_filter
# 检查错误
sudo modprobe usb_filter
# 检查 UEFI 变量是否可访问
ls /sys/firmware/efi/efivars/
# 检查 EFI 变量挂载
mount | grep efivarfs
# 检查安全启动状态
mokutil --sb-state
通过增加内核详细程度启用调试消息:
# 增加内核日志级别
echo 8 | sudo tee /proc/sys/kernel/printk
# 或使用动态调试
echo 'module usb_filter +p' | sudo tee /sys/kernel/debug/dynamic_debug/control
# 查找设备的 VID:PID
lsusb
# 输出:Bus 001 Device 002: ID 0781:5591 SanDisk Corp. Ultra
# 添加到黑名单
echo "0781:5591" | sudo tee /sys/kernel/usb_filter/add
# 验证
cat /sys/kernel/usb_filter/list
# 输出:0781:5591
# 添加多个设备
echo "0781:5591,0a12:0001,1234:5678" | sudo tee /sys/kernel/usb_filter/add
# 检查黑名单
cat /sys/kernel/usb_filter/list
# 输出:0781:5591,0a12:0001,1234:5678
# 临时禁用过滤
echo "0" | sudo tee /sys/kernel/usb_filter/enable
# 重新启用
echo "1" | sudo tee /sys/kernel/usb_filter/enable
- 修改黑名单需要 root 权限
- UEFI 变量受固件安全策略保护
- 模块使用 kprobes,需要适当的内核权限
- 自定义 GUID 防止与系统 EFI 变量冲突,这里有个坑,UEFI全局GUID,可以在efivar文件系统内看到,但是没有权限写,私有GUID可以写但是无法被看到。
本项目采用 GPL-2.0 许可证 - 查看 LICENSE 文件了解详情。
- Linux 内核社区提供 kprobe 基础设施
- UEFI 论坛提供 EFI 变量规范
- DKMS 项目提供动态内核模块支持
注意:此模块会修改内核行为,应谨慎使用。在生产系统部署前,请始终在安全环境中进行测试。