USB Hook 模块 —— Based on LibUSB(Linux)

LibUSB简介

libusb 是一个用户态库,它通过 usbfs(又名 usbdevfs)与内核中的 USB 栈交互,主要依赖 ioctl 机制来实现控制、批量、同步等传输。libusb 最终是通过系统调用 ioctl 与内核中的 /dev/bus/usb 设备节点进行通信其中:

1
2

ioctl(fd, USBDEVFS_SUBMITURB, struct usbdevfs_urb *urb);

这个调用是 libusb 进行异步传输时最常用的操作,而同步传输(如 libusb_bulk_transfer, libusb_control_transfer 等)也是通过同步的 SUBMITURB + REAPURB 来完成的。

libusb的调用链层:

1
2
3
4
5
6
7
8
9
10
11
12
13
14

libusb_bulk_transfer / libusb_control_transfer

libusb_submit_transfer()

op_submit_transfer() // backend,比如 linux_usbfs_submit_transfer()

ioctl(fd, USBDEVFS_SUBMITURB, &urb)

内核:usbdev_do_ioctl()

内核:usbdevfs_submit_urb()等

内核:usb_hcd_submit_urb()

Hook函数架构

usbdev_do_ioctl(), 它的路径在drivers/usb/core/devio.c,功能是处理来自用户态的ioctl调用,是所有 USB 传输的总入口。如果想知道上位机通过 libusb 或 usbfs 向外设发送的具体数据内容(例如 controlbulkinterrupt 传输的数据),你需要在 usbdev_do_ioctl 中拦截下特定的 IOCTL 命令,如:

  • USBDEVFS_CONTROL(控制传输)
  • USBDEVFS_BULK(批量传输)
  • USBDEVFS_SUBMITURB(异步传输,核心数据封装在 URB 中)

可以通过hook 判断 cmd 类型,并从用户态通过 arg 拷贝出传输数据,架构如下:

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

模块加载:
└─ 查找 usbdev_do_ioctl 的地址(通过 kallsyms)
└─ 用 ftrace 设置 hook:每次用户调用 usbdev_do_ioctl,转而调用我们的 handler

hook_handler(regs):
└─ 获取 IOCTL 参数:
- file
- cmd
- arg(指向结构体)

└─ 判断传输类型:
├─ if cmd == USBDEVFS_CONTROL:
│ └─ 从用户空间 copy ctrltransfer 结构体
│ └─ 判断是写方向 (OUT)
│ └─ 打印传输方向 + 数据内容

├─ elif cmd == USBDEVFS_BULK:
│ └─ copy bulktransfer 结构体
│ └─ 如果是 OUT(ep & 0x80 == 0)
│ └─ 打印 endpoint 和数据内容

└─ elif cmd == USBDEVFS_SUBMITURB:
└─ copy urb 结构体
└─ 判断 type(控制/批量/中断)
└─ 如果是 OUT,打印 endpoint 和 buffer 数据

模块卸载:
└─ 取消 hook,恢复原始函数

Tips: 关于同步传输和异步传输的说明

类型 描述 使用的 IOCTL 命令 底层数据结构 调用后行为
同步传输(阻塞) 用户态程序调用 libusb_bulk_transfer()libusb_control_transfer() 这类接口时,会阻塞等待传输完成,直到内核返回结果。 USBDEVFS_BULK, USBDEVFS_CONTROL, USBDEVFS_INTERRUPT usbdevfs_bulktransfer, usbdevfs_ctrltransfer 调用时阻塞直到完成
异步传输(非阻塞) 用户态程序使用 libusb_submit_transfer() 提交一个异步任务(URB),不会等待传输完成,而是由内核在完成后发出通知(通过回调或 poll)。 USBDEVFS_SUBMITURB, USBDEVFS_DISCARDURB usbdevfs_urb 立即返回,不阻塞,内核异步完成后再通知