【问题标题】:Make USB device visible with different vendor and product ID使用不同的供应商和产品 ID 使 USB 设备可见
【发布时间】:2021-07-11 05:34:31
【问题描述】:

我正在寻找一种使 USB 设备显示为具有不同供应商和产品 ID 的方法。我正在尝试制作一个专有软件来与应该支持但仅仅因为它的 ID 而被拒绝的 USB 设备一起使用。

该软件适用于 Windows,但我可以在 Linux 的 VM 中运行它。因此,无论哪种方法,我都可以接受:

  • 在 Linux 中更改 USB ID
  • 在 Windows 中更改 USB ID
  • 让 Qemu(或其他类似的)在 passthrough 中更改 USB ID

【问题讨论】:

    标签: usb emulation device-emulation


    【解决方案1】:

    可能有更简单的方法可以做到这一点,但我遇到了类似的问题,并且能够创建一个进程,在该进程中我可以更改设备描述符信息以用于开发目的。流程总结在这张图中:

    首先为您的 Raspberry Pi 配置一个静态 IP 地址并配置您的 PC 以太网 TCP/IPv4 设置,以便您能够通过 LAN 连接与您的 Raspberry Pi 通信。

    从 Virtual Here website 为您的 PC 下载 Virtual Here Raspberry Pi 服务器和客户端软件。试用版适用于此用例。

    将 Virtual Here 服务器软件移至您的 Raspberry Pi。为了运行 USB 服务器,您需要使用 $ sudo chmod +x vhusbdarm 更改文件的权限,然后使用 $ sudo ./vhusbdarm 运行。

    在本地计算机上运行客户端软件。您将看到客户端在 USB 设备服务器上的<Your Raspberry Pi IP address>:7575 上检测到 USB 设备。此时连接到设备不会带来任何好处,而是模拟直接连接。

    运行下面的 python 文件,它是从我找到的解决方案 here 修改的,但在转发原始数据之前利用 Scapy 嗅探来捕获传入的数据包。链接解决方案中的原始脚本也应该可以正常工作。在脚本中你可以看到我使用了端口12345

    #!/usr/bin/env python
    
    from scapy.all import *
    import socket
    import threading
    import select
    from queue import Queue
    main_queue = Queue()
    
    terminateAll = False
    
    class ClientThread(threading.Thread):
        def __init__(self, clientSocket, targetHost, targetPort):
            threading.Thread.__init__(self)
            self.__clientSocket = clientSocket
            self.__targetHost = targetHost
            self.__targetPort = targetPort
    
        def run(self):
            print("Client Thread started")
    
            self.__clientSocket.setblocking(0)
    
            targetHostSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            targetHostSocket.connect((self.__targetHost, self.__targetPort))
            targetHostSocket.setblocking(0)
    
            clientData = b""
            targetHostData = b""
            terminate = False
            while not terminate and not terminateAll:
                inputs = [self.__clientSocket, targetHostSocket]
                outputs = []
    
                if len(clientData) > 0:
                    outputs.append(self.__clientSocket)
    
                if len(targetHostData) > 0:
                    outputs.append(targetHostSocket)
    
                try:
                    inputsReady, outputsReady, errorsReady = select.select(inputs, outputs, [], 1.0)
                except Exception as e:
                    print(e)
                    break
    
                for inp in inputsReady:
                    if inp == self.__clientSocket:
                        try:
                            data = self.__clientSocket.recv(4096)
                            #print(data)
                            #data = b""
                            #while not main_queue.empty():
                            #    data += main_queue.get()
                        except Exception as e:
                            print(e)
    
                        if data != None:
                            if len(data) > 0:
                                targetHostData += data
                            #else:
                            #    terminate = True
                    elif inp == targetHostSocket:
                        try:
                            data = b""
                            while not main_queue.empty():
                               data += main_queue.get()
                        except Exception as e:
                            print(e)
    
                        if data != None:
                            if len(data) > 0:
                                clientData += data
    
                for out in outputsReady:
                    if out == self.__clientSocket and len(clientData) > 0:
                        #pck = Ether(clientData)
                        #pck.show()
                        bytesWritten = self.__clientSocket.send(clientData)
                        if bytesWritten > 0:
                            clientData = clientData[bytesWritten:]
                    elif out == targetHostSocket and len(targetHostData) > 0:
                        #pck = Ether(targetHostData)
                        #pck.show()
                        bytesWritten = targetHostSocket.send(targetHostData)
                        if bytesWritten > 0:
                            targetHostData = targetHostData[bytesWritten:]
    
            self.__clientSocket.close()
            targetHostSocket.close()
            print ("ClientThread terminating")
    
    
    def handle_sniff(pck):
        if IP in pck:
            if pck[IP].src == "192.168.1.48":
                if Raw in pck:
                    payload = pck[Raw].load
                    if b'\x12\x01\x00\x01\x00\x00\x00\x08$\x07\x04\x00\x88#\x01\x02\x00\x01' in payload:
                        payload = payload.replace(b'\x00\x08$\x07\x04\x00\x88#\x01\x02\x00', b'\x00\x08$\x07\x04\x00\x88\x15\x01\x02\x00')
                        
                    print(payload)
                    main_queue.put(payload)
    
    
    if __name__ == '__main__':
    
        localHost = "localhost"
        localPort = 12345
        targetHost = "192.168.1.12"
        targetPort = 7575
    
        serverSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        serverSocket.bind((localHost, localPort))
        serverSocket.listen(5)
        print("Waiting for client...")
        while True:
            try:
                clientSocket, address = serverSocket.accept()
            except KeyboardInterrupt:
                print("\nTerminating...")
                terminateAll = True
                break
            print("starting client")
            ClientThread(clientSocket, targetHost, targetPort).start()
            sniff(iface="Ethernet", prn=lambda pck: handle_sniff(pck))
    
        serverSocket.close()
    

    脚本运行后,配置 Virtual Here 客户端以侦听位于 localhost:12345 的 USB 服务器。 handle_sniff 函数是更改 USB 设备描述符信息的地方。连接后,您应该能够双击下拉树中的 USB 设备。您将看到 USB 数据开始在您的 python 控制台中打印。

    在上面的示例中,我更改了 USB 描述符的设备 bcdDevice 字节。

    我用来识别包含我所针对的信息的数据包的另一个有用脚本如下。我修改了在此solution 中找到的脚本。它被修改为打印原始数据以及解包的设备描述符信息,然后可以在 TCP 原始数据中搜索以确定要替换的字节。

    #!/usr/bin/env python
    
    from __future__ import print_function
    import argparse
    import string
    import struct
    import sys
    
    import win32api
    import win32file
    import pywintypes
    
    BUFF=b""
    def CTL_CODE(DeviceType, Function, Method, Access):
        return (DeviceType << 16) | (Access << 14) | (Function << 2) | Method
    def USB_CTL(id):
       # CTL_CODE(FILE_DEVICE_USB, (id), METHOD_BUFFERED, FILE_ANY_ACCESS)
        return CTL_CODE(0x22, id, 0, 0)
    
    IOCTL_USB_GET_ROOT_HUB_NAME = USB_CTL(258)                   # HCD_GET_ROOT_HUB_NAME
    IOCTL_USB_GET_NODE_INFORMATION = USB_CTL(258)                # USB_GET_NODE_INFORMATION
    IOCTL_USB_GET_NODE_CONNECTION_INFORMATION = USB_CTL(259)     # USB_GET_NODE_CONNECTION_INFORMATION
    IOCTL_USB_GET_NODE_CONNECTION_DRIVERKEY_NAME = USB_CTL(264)  # USB_GET_NODE_CONNECTION_DRIVERKEY_NAME
    IOCTL_USB_GET_NODE_CONNECTION_NAME = USB_CTL(261)            # USB_GET_NODE_CONNECTION_NAME
    IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION = USB_CTL(260) # USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION
    
    USB_CONFIGURATION_DESCRIPTOR_TYPE = 2
    USB_STRING_DESCRIPTOR_TYPE = 3
    USB_INTERFACE_DESCRIPTOR_TYPE = 4
    MAXIMUM_USB_STRING_LENGTH = 255
    
    
    def open_dev(name):
        try:
            handle = win32file.CreateFile(name,
                                      win32file.GENERIC_WRITE,
                                      win32file.FILE_SHARE_WRITE,
                                      None,
                                      win32file.OPEN_EXISTING,
                                      0,
                                      None)
        except pywintypes.error as e:
            return None
        return handle
    
    
    def get_root_hub_name(handle):
        buf = win32file.DeviceIoControl(handle,
                                    IOCTL_USB_GET_ROOT_HUB_NAME,
                                    None,
                                    6,
                                    None)
        act_len, _ = struct.unpack('LH', buf)
        buf = win32file.DeviceIoControl(handle,
                                    IOCTL_USB_GET_ROOT_HUB_NAME,
                                    None,
                                    act_len,
                                    None)
        return buf[4:].decode('utf-16le')
    
    
    def get_driverkey_name(handle, index):
        key_name = bytes(chr(index) + '\0'*9, 'utf-8')
        try:
            buf = win32file.DeviceIoControl(handle,
                                        IOCTL_USB_GET_NODE_CONNECTION_DRIVERKEY_NAME,
                                        key_name,
                                        10,
                                        None)
        except pywintypes.error as e:
            print(e.strerror, index)
            sys.exit(1)
        _, act_len, _ = struct.unpack('LLH', buf)
        buf = win32file.DeviceIoControl(handle,
                                    IOCTL_USB_GET_NODE_CONNECTION_DRIVERKEY_NAME,
                                    key_name,
                                    act_len,
                                    None)
        return buf[8:].decode('utf-16le')
    
    
    def get_ext_hub_name(handle, index):
        hub_name = chr(index) + '\0'*9
        buf = win32file.DeviceIoControl(handle,
                                    IOCTL_USB_GET_NODE_CONNECTION_NAME,
                                    hub_name,
                                    10,
                                    None)
        _, act_len, _ = struct.unpack('LLH', buf)
        buf = win32file.DeviceIoControl(handle,
                                    IOCTL_USB_GET_NODE_CONNECTION_NAME,
                                    hub_name,
                                    act_len,
                                    None)
        return buf[8:].decode('utf-16le')
    
    
    def get_str_desc(handle, conn_idx, str_idx):
        req = struct.pack('LBBHHH',
                      conn_idx,
                      0,
                      0,
                      (USB_STRING_DESCRIPTOR_TYPE<<8) | str_idx,
                      win32api.GetSystemDefaultLangID(),
                      MAXIMUM_USB_STRING_LENGTH)
        try:
            buf = win32file.DeviceIoControl(handle,
                                        IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION,
                                        req,
                                        12+MAXIMUM_USB_STRING_LENGTH,
                                        None)
        except pywintypes.error as e:
             return 'ERROR: no String Descriptor for index {}'.format(str_idx)
        if len(buf) > 16:
            return buf[14:].decode('utf-16le')
        return ''
    
    
    def exam_hub(name, verbose, level):
        handle = open_dev(r'\\.\{}'.format(name))
        if not handle:
            print('Failed to open device {}'.format(name))
            return
        buf = win32file.DeviceIoControl(handle,
                                    IOCTL_USB_GET_NODE_INFORMATION,
                                    None,
                                    76,
                                    None)
        print_hub_ports(handle, ord(buf[6]), verbose, level)
        handle.close()
    
    
    def print_str_or_hex(to_be_print):
        if all(c in string.printable for c in to_be_print):
            print('"{}"'.format(to_be_print))
            return
        print('Hex: ', end='')
        for x in to_be_print:
            print('{:02x} '.format(ord(x)), end='')
        print('')
    
    
    def print_hub_ports(handle, num_ports, verbose, level):
        print(handle, num_ports, verbose, level)
        for idx in range(1, num_ports+1):
            info = bytes(chr(idx) + '\0'*34, 'utf-8')
            try:
                buf = win32file.DeviceIoControl(handle,
                                            IOCTL_USB_GET_NODE_CONNECTION_INFORMATION,
                                            info,
                                            34 + 11*30,
                                            None)
            except pywintypes.error as e:
                print(e)
                print(e.winerror, e.funcname, e.strerror)
                return
            print(buf)
            _, vid, pid, vers, manu, prod, seri, _, ishub, _, stat = struct.unpack('=12sHHHBBB3s?6sL', buf[:35])
    
            if ishub:
                if verbose:
                    print('{}  [Port{}] {}'.format('  '*level, idx, 'USB Hub'))
                exam_hub(get_ext_hub_name(handle, idx), verbose, level)
            elif stat == 0 and verbose:
                print('{}  [Port{}] {}'.format('  '*level, idx, 'NoDeviceConnected'))
            elif stat == 1:
                if verbose or (manu != 0 or prod != 0 or seri != 0):
                    print('{}  [Port{}] {}'.format('  '*level, idx, get_driverkey_name(handle, idx)))
                    print('{}    Vendor ID:    0x{:04X}'.format('  '*level, vid))
                    print('{}    Product ID:  0x{:04X}'.format('  '*level, pid))
                    print('{}    Device BCD:  0x{:04X}'.format('  '*level, vers))
                    print(vers)
                    if manu != 0:
                        print('{}    Manufacturer (0x{:x}) -> '.format('  '*level, manu), end='')
                        print_str_or_hex(get_str_desc(handle, idx, manu))
                    if prod != 0:
                        print('{}    Product      (0x{:x}) -> '.format('  '*level, prod), end='')
                        print_str_or_hex(get_str_desc(handle, idx, prod))
                    if seri != 0:
                        print('{}    Serial No    (0x{:x}) -> '.format('  '*level, seri), end='')
                        print_str_or_hex(get_str_desc(handle, idx, seri))
    
    
    def main():
        parser = argparse.ArgumentParser()
        parser.add_argument('-v', '--verbose', action='store_true',
                        help="Increase output verbosity.")
        args = parser.parse_args()
    
        for i in range(10):
            name = r"\\.\HCD{}".format(i)
            handle = open_dev(name)
            if not handle:
                continue
    
            root = get_root_hub_name(handle)
            print('{}RootHub: {}'.format('\n' if i != 0 else '', root))
    
            dev_name = r'\\.\{}'.format(root)
            dev_handle = open_dev(dev_name)
            if not dev_handle:
                print('Failed to open device {}'.format(dev_name))
                continue
    
            buf = win32file.DeviceIoControl(dev_handle,
                                        IOCTL_USB_GET_NODE_INFORMATION,
                                        None,
                                        76,
                                        None)
            global BUFF
            BUFF=buf
            print_hub_ports(dev_handle, buf[6], args.verbose, 0)
            dev_handle.close()
            handle.close()
    
    if __name__ == '__main__':
        main()
    

    附:这对于过滤和修改正在传输的任何 USB 数据也非常有帮助,而不仅仅是设备描述符。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-25
      • 2011-01-02
      • 1970-01-01
      相关资源
      最近更新 更多