关于通过UART端口获取全协议BLE原始数据包、实现BLE代理及修改btusb驱动可行性的技术咨询
Great question—let's break this down step by step, covering your capture needs, proxy goals, and the feasibility of modifying the btusb driver.
Can I Capture Full BLE Stack Packets via UART/BLE Adapter or nRF52 DK?
Absolutely. Here's how:
- nRF52 DK: It natively supports sniffer mode, and you can configure it to output raw BLE frames (covering LL, SMP, ATT, L2CAP) over UART. The raw binary data can be directly parsed and modified in Python—you can use Nordic's nRF Sniffer tool with Wireshark for visualization, or write a simple UART reader to capture frames programmatically.
- TP-Link BLE Adapter: This uses a standard USB Bluetooth chip (likely Realtek/Broadcom). The default
btusbdriver doesn't expose full raw stack frames out of the box, but if you're building a proxy, the driver modification or user-space socket approach below will solve this. For basic capture, tools likebtmoncan log LL+ layers, though you'll need to extract raw binary data manually.
BLE Proxy: Alternative to Sniffing
Your goal of building a BLE proxy (acting as a middleman to forward and log all traffic between two BLE devices) is far more reliable than sniffing (which can miss packets). The core idea is to have your proxy device act as a Central connecting to the remote Peripheral, while simultaneously acting as a Peripheral that your local host connects to. You'll then bidirectionally forward all packets between the two links.
Is Modifying the btusb Driver Feasible?
Yes, but it's an advanced approach. Here's the breakdown:
- The
btusbdriver handles low-level USB-Bluetooth communication and exposes the HCI interface to the BlueZ stack above. - To implement proxy functionality, you'd need to intercept HCI commands, events, and ACL data frames at the driver level, then forward them to a second BLE adapter while logging the data.
- That said, a user-space solution using BlueZ's raw HCI sockets is usually better—no kernel modifications needed, easier to develop/debug, and sufficient for most proxy use cases. Only modify the driver if you need ultra-low latency or hardware-level filtering.
Implementation Approaches & Code Examples
Option 1: User-Space Proxy with Raw HCI Sockets (Recommended)
This uses Python and raw HCI sockets to capture, forward, and log packets without touching the kernel.
Step-by-Step Overview:
- Open raw HCI sockets for both BLE adapters.
- Configure sockets to capture all HCI frames (commands, events, ACL data).
- Build a loop to forward frames between the two adapters and log raw data.
- Handle connection state synchronization to keep both links alive.
Python Code Snippet (Basic Proxy Framework):
import socket import struct from typing import Optional # HCI socket filter constant to capture all frames HCI_FILTER_MASK = struct.pack("III", 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF) def open_hci_socket(device_id: int) -> socket.socket: """Open a raw HCI socket bound to the specified Bluetooth device ID""" sock = socket.socket(socket.AF_BLUETOOTH, socket.SOCK_RAW, socket.BTPROTO_HCI) sock.bind((device_id,)) sock.setsockopt(socket.SOL_HCI, socket.HCI_FILTER, HCI_FILTER_MASK) return sock def proxy_frames(sock_a: socket.socket, sock_b: socket.socket, log_file: Optional[object] = None): """Bidirectional frame forwarding between two HCI sockets""" while True: # Forward from sock_a to sock_b data, _ = sock_a.recvfrom(4096) if log_file: log_file.write(f"[Adapter A -> B] {data.hex()}\n") sock_b.send(data) # Forward from sock_b to sock_a data, _ = sock_b.recvfrom(4096) if log_file: log_file.write(f"[Adapter B -> A] {data.hex()}\n") sock_a.send(data) if __name__ == "__main__": # Replace with your adapter IDs (find via `bluetoothctl show` or `hciconfig`) ADAPTER_A_ID = 0 ADAPTER_B_ID = 1 try: sock_a = open_hci_socket(ADAPTER_A_ID) sock_b = open_hci_socket(ADAPTER_B_ID) with open("ble_proxy_logs.txt", "a") as log: print("BLE proxy running... Press Ctrl+C to stop") proxy_frames(sock_a, sock_b, log) except KeyboardInterrupt: print("\nProxy stopped.") sock_a.close() sock_b.close()
Option 2: btusb Driver Modification (Advanced)
If you must implement the proxy at the driver level, here's a high-level approach:
Key Steps:
- Extend the
struct btusb_deviceto add a pointer to the proxy adapter'sbtusb_deviceinstance and a log file handle. - Hook the
btusb_send_frameandbtusb_recv_framefunctions to intercept frames. - Forward intercepted frames to the proxy adapter and log raw data (either to kernel logs or a user-space netlink socket).
- Handle concurrency and synchronization to avoid deadlocks.
Kernel Code Snippet (Core Interception Logic):
#include <linux/hci.h> #include <linux/usb.h> #include <linux/fs.h> // Extend btusb_device with proxy fields struct btusb_device { // Existing fields... struct btusb_device *proxy_dev; struct file *log_file; }; // Store original send function pointer static int (*original_btusb_send_frame)(struct hci_dev *hdev, struct sk_buff *skb); // Hooked send function static int btusb_proxy_send_frame(struct hci_dev *hdev, struct sk_buff *skb) { struct btusb_device *bt_dev = hci_get_drvdata(hdev); // Forward to proxy adapter if configured if (bt_dev->proxy_dev) { struct sk_buff *clone = skb_clone(skb, GFP_KERNEL); if (clone) { hci_send_frame(bt_dev->proxy_dev->hdev, clone); // Log raw frame data if (bt_dev->log_file) { loff_t pos = bt_dev->log_file->f_pos; kernel_write(bt_dev->log_file, skb->data, skb->len, &pos); bt_dev->log_file->f_pos = pos; } } } // Call original send function return original_btusb_send_frame(hdev, skb); } // Initialize hook in btusb_probe static int btusb_probe(struct usb_interface *intf, const struct usb_device_id *id) { // Existing probe logic... original_btusb_send_frame = hdev->send_frame; hdev->send_frame = btusb_proxy_send_frame; // Add logic to set proxy_dev and log_file via module parameters or sysfs return 0; }
Critical Notes
- Ensure both BLE adapters support dual role (Central + Peripheral mode)—most modern adapters do, but verify with
bluetoothctl show. - User-space solutions require root privileges to access raw HCI sockets.
- Driver modifications require kernel module development skills and compiling against your running kernel version.
- For encrypted SMP traffic, your proxy will need to handle key exchange to decrypt/re-encrypt frames—this adds significant complexity, so start with unencrypted connections first.
内容的提问来源于stack exchange,提问作者Dev

