使用pyusb读取USBKEY的PIN码

USB Key是一种USB接口的硬件设备。它内置单片机或智能卡芯片,有一定的存储空间,可以存储用户的私钥以及数字证书,利用USB Key内置的公钥算法实现对用户身份的认证。由于用户私钥保存在密码锁中,理论上使用任何方式都无法读取,因此保证了用户认证的安全性。

本文主要记录使用python中pyusb模块读写USBKEY中的信息过程。以便在以后使用。

0x00 USBKEY采用的设备通信规范

在操作USBKEY之前,先来了解下USBKEY使用的设备通信规范。
USB设备定义很多类型代码,有关于音频、视频、图片等等,详细了解请参考:http://www.usb.org/developers/defined_class
在这里主要关注设备描述符这一类型代码。主机与USB设备通信的时候,相对于主机来说,就是输入输出,从主机到USB设备传输数据,叫输出。从USB设备到主机传输数据,叫输入。无论是输入还是输出主机都会给USB设备发送配置包,第一个配置包就是设备描述符的配置包,目的是获取插入的USB属性信息,以便加载合适的驱动程序,或者读取USB设备信息。现在就来详细地分析一下设备描述符包的定义:

配置包格式

偏移 字段 大小 值类型 描述
0 bmRequestType 1 Bitmap 请求特征:D7:传输方向 0=主机至设备 1=设备至主机。D6-D5:种类 0=标准 1=类 2=厂商 3=保留。D4..D0:接受者 0=设备 1=接口 2=端点 3=其他 4-31=保留
1 bRequest 1 Value 请求类别
2 wValue 2 Value 字长域,根据不同的请求含义改变
4 wIndex 2 索引或偏移 字长域,根据不同的请求含义改变.典型用于传送索引或偏移.
6 wLength 2 数值 如有数据传送阶段,此为数据字节 数.
  • bmRequestType
    这个域表明此请求的特性,特别地这个域表明了第二阶段的控制传输方向。如果wLength域被设作0的话表明没有数据传送阶段,那Direction位就会被忽略。除此之外,这个域还指明了设备类型,以及接收者,如:接口、端节点等等。
  • bRequest域
    这个域标识特别的请求。bmRequestType域的Type位可修改此域的含义本,仅当bmRequestType的Type位为0时,该设定标识标准请求。
    编码如下:
描述
GET_STATUS 0
CLEAR_FEATURE 1
Reserved for future use 2
SET_FEATURE 3
Reserved for future use 4
SET_ADDRESS 5
GET_DESCRIPTOR 6
SET_DESCRIPTOR 7
GET_CONFIGURATION 8
SET_CONFIGURATION 9
GET_INTERFACE 10
SET_INTERFACE 11
SYNCH_FRAME 12
  • wValue
    这个域的内容随着请求不同而变,用来向设备传送请求的参数。
    设备描述符里这个字段的低字节表示描述符的索引即从哪里开始读取设备描述符,高字节表示描述符的类型(小端对齐)。高字节的类型如下:
描述
DEVICE 1
CONFIGURATION 2
STRING 3
INTERFACE 4
ENDPOINT 5
DEVICE_QUALIFIER 6
OTHER_SPEED_CONFIGURATION 7
INTERFACE_POWER1 8
  • wIndex
    这个域的内容随着请求不同而变,用来向设备传送请求的参数。
    这个域还经常用于表明是哪一个接口或端结点,在获取描述符里,设置为0。

0x02 安装pyusb

使用pip安装

pip install pyusb

windows下需要安装libusb,下载地址:https://sourceforge.net/projects/libusb-win32/?source=typ_redirect
windows下兼容性不是很好,有些操作不能实现,建议在linux下使用。

linux下:

sudo apt-get install python-usb
pip install pyusb

0x03 使用pyusb读取USBKEY中信息。

基本使用

参考:https://github.com/walac/pyusb/blob/master/docs/tutorial.rst

首先这里声明下,并不是所有的USB都已做成USBKEY,这里USBKEY都有个共同的特性:其VendorIDProductID都为0x3689,即:VID=PID=13961.

这里贴上我写的获取USB描述符基本信息的代码:

import sys
import usb.core
import usb.backend.libusb1
backend = usb.backend.libusb1.get_backend(find_library=lambda x: "libusb-1.0.dll")
dev = usb.core.find(idVendor=0x3689, idProduct=0x3689)
dev.set_configuration()
sys.stdout.write(
'VendorID=' + str(dev.idVendor) + '\n' +
'Hexadecimal VendorID=' + hex(dev.idVendor) + '\n' +
'ProductID=' + str(dev.idProduct) + '\n' +
'Hexadecimal ProductID=' + hex(dev.idProduct) + '\n' +
'Serial=' + str(dev.iSerialNumber) + '\n' +
'Hexadecimal Serial=' + hex(dev.iSerialNumber) + '\n' +
'bLength=' + str(dev.bLength) + '\n' +
'Hexadecimal bLength=' + hex(dev.bLength) + '\n' +
'bDescriptorType=' + str(dev.bDescriptorType) + '\n' +
'Hexadecimal bDescriptorType=' + hex(dev.bDescriptorType) + '\n' +
'bcdUSB=' + str(dev.bcdUSB) + '\n' +
'Hexadecimal bcdUSB=' + hex(dev.bcdUSB) + '\n' +
'bDeviceClass=' + str(dev.bDeviceClass) + '\n' +
'Hexadecimal bDeviceClass=' + hex(dev.bDeviceClass) + '\n' +
'bDeviceSubClass=' + str(dev.bDeviceSubClass) + '\n' +
'Hexadecimal bDeviceSubClass=' + hex(dev.bDeviceSubClass) + '\n' +
'bDeviceProtocol=' + str(dev.bDeviceProtocol) + '\n' +
'Hexadecimal bDeviceProtocol=' + hex(dev.bDeviceProtocol) + '\n' +
'bMaxPacketSize0=' + str(dev.bMaxPacketSize0) + '\n' +
'Hexadecimal bMaxPacketSize0=' + hex(dev.bMaxPacketSize0) + '\n' +
'bcdDevice=' + str(dev.bcdDevice) + '\n' +
'Hexadecimal bcdDevice=' + hex(dev.bcdDevice) + '\n' +
'iManufacturer=' + str(dev.iManufacturer) + '\n' +
'Hexadecimal iManufacturer=' + hex(dev.iManufacturer) + '\n' +
'bNumConfigurations=' + str(dev.bNumConfigurations) + '\n' +
'Hexadecimal bNumConfigurations=' + hex(dev.bNumConfigurations) + '\n' +
'address=' + str(dev.address) + '\n' +
'Hexadecimal address=' + hex(dev.address) + '\n' +
'bus=' + str(dev.bus) + '\n' +
'Hexadecimal bus=' + hex(dev.bus) + '\n' +
'port_number=' + str(dev.port_number) + '\n' +
'Hexadecimal port_number=' + hex(dev.port_number) + '\n' +
'port_numbers=' + str(dev.port_numbers) + '\n' +
'speed=' + str(dev.speed) + '\n' +
'Hexadecimal speed=' + hex(dev.speed) + '\n'
)

描述符含义:

idVendor:厂商ID,由USB-IF分配,需要向USB-IF组织申请。
idProduct:产品ID,由厂商指定。
iSerialNumber: 用于描述产品序列号的字符串描述符索引,注意,所有的字符串描述符是可选的,如果没有字符串描述符,指定这些索引为0x00
bLength: 描述符的长度,设备描述符的长度为18个字节。
bDescriptorType: 描述符的类型,设备描述符的类型为0x01
bcdUSB: USB设备所遵循的协议版本号,例如2.0协议为0x0200
bDeviceClass: USB设备类代码,由USB-IF分配,如果该字段为0x00,表示由接口描述符来指定(有可能该USB设备是一个复合设备,USB设备的各个接口相互独立,分别属于不同的设备类)。如果是0x01~0xfe,表示为USB-IF定义的设备类,例如0x03为HID设备,0x09为HUB设备。如果是0xff,表示由厂商自定义设备类型。
bDeviceProtocol: 协议代码,由USB-IF分配,如果bDeviceClass和bDeviceSubClass定义为0x00,那么该字段也必须为0x00。
bcdDevice: 设备序列号,由厂商自行设置。
iManufacturer: 用于描述厂商的字符串描述符索引。
bNumConfigurations:配置描述符数量。
address :端点地址
portnumber :端点端口

以上代码只是获取USBKEY的基本信息。如果想要读取USBKEY的唯一标识,则需要使用控制传输的方法来操作。在这里介绍一个常用的唯一标识的构成,即根据isSeriesNumber读取产品序列号。

控制传输方法介绍:
dev.ctrl_transfer(bmRequestType,bRequest,wValue,wIndex,wLength,timeout)
其中参数文中都介绍过,这里不做赘述。
获取USBKEY 唯一标识如下:

代码如下:

#!/usr/bin/python
import sys
import usb.core
import usb.backend.libusb1
import hashlib
# find USB devices
backend = usb.backend.libusb1.get_backend(find_library=lambda x: "libusb-1.0.dll")
dev = usb.core.find(idVendor=0x3689, idProduct=0x3689)
if dev.is_kernel_driver_active(0):
dev.detach_kernel_driver(0)
dev.set_configuration()
b = bytearray([0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
buff1 = dev.ctrl_transfer(0x21, 0x9, 0x0302, 0, b, 25)
buff2 = dev.ctrl_transfer(0xa1, 0x1, 0x0301, 0, 0x0115, 0)
id1 = (buff2[3] | (buff2[2] << 8) | (buff2[1] << 16) | (buff2[0] << 24))
id2 = (buff2[7] | (buff2[6] << 8) | (buff2[5] << 16) | (buff2[4] << 24))
keyid = (hex(id1) + hex(id2)).replace('0x', '')
keyid = keyid.upper()
m = hashlib.md5()
keytem = m.update(keyid)
key = m.hexdigest().upper()
return str(keyid).strip(), str(key).strip()

参考链接:

pyusb github

USB描述符【整理】

USB协议深入分析 设备描述符配置包 和 返回设备描述符

USB协议深入分析 设置USB地址

文章目录
  1. 1. 0x00 USBKEY采用的设备通信规范
    1. 1.1. 配置包格式
  2. 2. 0x02 安装pyusb
  3. 3. 0x03 使用pyusb读取USBKEY中信息。
    1. 3.1. 基本使用
    2. 3.2. 参考链接: