U盘是我们最常使用的一种USB设备,本文使用DOSUSB做驱动,试图以读取扇区的方式读取你的U盘。

温馨提示

本文涉及的协议可能会比较多。

了解你的U盘

首先我们用程序usbview.exe去看一下你的U盘,我在本文中用于测试的U盘情况如下:

Device Descriptor: (设备描述符)

USB Address:1

Length:18

Descriptor Type:1

USB Specification nr.:0x0110

Calss Code:Class code specified by interface

Subclass Code:0x00

Protocol Code:0x00

MAX Packet Size:0x08

Vendor ID:0x058f

Product ID:0x9321

Device Code:0x0100

Manufacture Index:1

Product Index:2

Serial Number Index:0

Number of Configuration:1

String Descriptor: (字符串描述符)

Manufacturer:Alcor Micro

Product:Mass Storage Device

Configuration Descriptor: (配置描述符)

Length:9

Descriptor Type:2

Total Length:32

Number of Interfaces:1

Configuration Value:1

Configuration Index:0

Attributes:Bus Powered

Max Power:50mA

Interface Descriptor: (接口描述符)

Length:9

Descriptor Type:4

Interface Number:0

Alternate Setting:0

Number of Endpoints:2

Interface Class:Mass Storage Device

Interface Sub Class:6

Interface Protocol:80

Interface Index:0

Endpoint Descriptor: (端点描述符)

Length:7

Descriptor Type:5

Endpoint Address:1 OUT endpoint

Attributes:Bulk

Max Packet Size:64

Interval:0

Endpoint Descriptor: (端点描述符)

Length:7

Descriptor Type:5

Endpoint Address:2 IN endpoint

Attributes:Bulk

Max Packet Size:64

Interval:0

各种描述符的含义可以翻阅USB的specification,这里就不多说了,我们从接口描述符开始就一些关键点进行一下说明。

首先看接口描述符:

  • Interface Class=8,表明是Mass Storage Device;

  • Sub Class=6,表明执行SCSI命令;

  • Interface Protocol=0x80,表明支持Bulk传输;

  • 另外,Number of Endpoints=2,表明有两个端点。

两个端点描述符要注意的是:

  • Endpoint Address=1的是OUT端点,Endpoint Address=2的是IN端点,有些可能会不一样;

  • 有些U盘可能还会有第三个端点,比如支持中断传输的U盘还会有一个Interrupt端点,不过这都没有关系。

我大概看了我手头有的5个U盘,都支持批量传输,且支持SCSI命令,所以,这可能是一个比较典型的例子,我们就以它为例。

CBW和CSW

本文将只针对我们经常使用的USB设备——U盘。如果你打算尝试本文所介绍的内容,请准备好一个U盘,什么样子的都行,或者是一个USB读卡器,不过要记得插一张卡进去。实际上,本文所载范例就是使用一个USB的CF卡读卡器完成的。

不用担心损害你的U盘中的数据,本文不会对U盘进行任何写操作,仅仅做一些读操作。

这个系列中我们需要针对U盘读更多的规范,如下:

  • Universal Serial Bus Mass Storage Class Specification Overview

http://blog.whowin.net/specification/usb_msc_overview_1.2.pdf

  • Universal Serial Bus Mass Storage Class -- Bulk-Only Transport

http://blog.whowin.net/specification/usbmassbulk_10.pdf

  • SCSI Primary Commands - 3(SPC-3)

http://blog.whowin.net/specification/SCSI_Primary_Commands-3.pdf

  • SCSI Block Commands - 2(SBC-2)

http://blog.whowin.net/specification/SCSI_Block_Command-2.pdf

不用为规范发愁,实际上,前两个规范都很短,其中第一个对实际编程没有什么作用,但最好看一下。第二个规范连目录一共22页,其中13页以前的内容可以跳过(很多和USB Specification中相同),第三个规范主要看第6章,第四个规范主要看第5章,后两个规范在编程时需要经常翻阅,以便了解你正在实现的SCSI命令的具体格式和参数。

本节我们主要介绍两个新的数据结构,这两个结构都是在第二个规范中定义的。

1CBW

第一个数据结构叫CBW(Command Block Wrapper)。

这个结构将承载具体的与设备有关的命令发送到设备上去,这个结构分成两部分,第一部分从byte[0]--byte[14]共15个字节,第而部分从byte[15]--byte[30]共16个字节,整个数据结构为31个字节。规范中并没有定义第二部分的内容,这是因为第二部分承载的具体的命令,既与命令集(SCSI命令集)有关,也与具体的命令有关,我们使用SCSI命令集,所以后16个字节的内容在前面提到的后面两个规范中有定义。

比如我们要向设备发出一个SCSI命令INQUIRY(我们姑且先不要管命令的含义),那么这个命令的结构在SPC-3的第142页有定义,如下:

对于SCSI INQUIRY这条命令而言,CBW的第二部分的定义就是上面的这六个字节,不同的命令,定义也会不同。

好,我们回到CSW的结构上来。

根据规范,dCBWSignature的值必须是0X43425355,其实就是USBC这几个字母倒过来,这是因为CBW的字符顺序是little endian,而我们PC机中的字符顺序是big endian,所以要颠倒一下,总之写dCBWSignature = 0X43425355就OK了。dCBWTag仅仅是一个标志,你可以填任何值。

这里要先说一下CSW(Command Status Wrapper),我们每发出一个命令,设备都会返回一个CSW(这个东东下面很快就要介绍了),以说明命令的执行状态,这个结构中也有Signature和Tag这两个字段,其中Tag字段和发出命令时CBW中的Tag字段相同,这样就可以区分这个CSW是和那个CBW对应的了,至于Signature,下面再说。

下一个字段是dCBWDataTransferLength,表示的是当这个命令发出后,我们希望设备返回数据的字符数或者我们要向设备传输的字符数,本文仅涉及从设备返回数据,不涉及向设备传输数据。

举例来说:我们发送INQIURY命令到设备,按照SPC-3第144页的说明,该命令返回的数据至少为36个字节,所以,此时这个字节应该填36。再如:我们读取U盘的一个扇区,如果扇区的长度是512个字节,那么这个字段就要填512。

再下来是bmCBWFlags字段,这个字段只有bit 7有意义,为0表示要向设备传输数据,为1表示要从设备获得数据。

bCBWLUN字段总是填0,因为绝大多数的U盘都不支持多LUN(Logical Unit Number),只有一个逻辑单元自然好吗就是0了。

bCBWCBLength字段是只CBW第二部分的长度,像前面举例的INQUIRY命令,长度为6个字节,则这个字段就应该填6,再如:READ(10)命令的长度是10个字节(SBC-2第42页有定义),这个字段当然要填10了。

2CSW

第二个要说的数据结构是CSW,当host向device发送一个CBW后,接着就可以从device收到数据(或者发数据到device),当接受完所需的的数据后,就可以从device获得一个CSW(Command Status Wrapper),CSW的结构如下:

前面说过,在CBW中的dCBWSignature的值恒为:0x43425355,得到的CSW中的dCSWSignature的值为:0x53425355,dCSWTag与dCBWTag中的一致。

在得到的CSW中,恒定有13个字节,bCSWStatus的定义如下:

发送命令和接收数据

我们知道USB协议中定义了三种传输方式,控制传输、批量传输、中断传输和实时传输,本文中将涉及批量传输。

我们在使用控制传输时,我们设置好URB启动传输事务,相应的结果将返回到制定得buffer中,批量传输没有那么简单,批量传输分为输出事务和输入事务,我们应该注意到,前面在看U盘的描述表时,在端点这一级有两个端点,一个叫OUT端点,一个叫IN端点,当我们启动一个输出事务时,一定要发送给OUT端点,当我们启动一个输入事务时,一定要发送到输入端点。下面我们简单描述一下如何启动批量传输事务。

在使用控制传输时,我们应该阅读过DOSUSB的说明,并且对URB结构比较熟悉,URB中有一个字段叫transation_type,当这个值为0x2d时为控制传输;当为0x69时为批量传输的IN事务;当为0xe1时为批量传输的OUT事务;当我们启动一个传输时,一定要正确地设置这个值。

我们以一个具体的例子来说明如何启动一个传输,我们以SCSI INQUIRY命令为了,关于这个命令的定义在SPC-3的第142页--157页有说明,篇幅很长,但绝大多数篇幅用来解释返回数据的含义,我们可以暂时不去理会。

首先我们要填写CBW结构,CBW结构的第一部分的填写前面已经说的很明白了,第二部分的定义在SPC-3的第142页,共有6个字节,我们要按照定义填写好,实际上只要填两个字段,一个是OPERATION CODE=0X12,第二个就是ALLOCATION CODE=36,表示需要返回36个字节的内容。

CBW填好后,我们开始填写URB,首先把CBW的偏移和段地址放到URB的buffer_off和buffer_seg中,把transation_type=0xe1,表示一个输出事务,注意把end_point字段一定要放OUT endpoint的地址,从前面的描述符表中看,应该是1(2是IN endpoint的地址,你的机器可能不同),填完以后调用DOSUSB,这样,一个承载着INQUIRY命令的输出事务就发送到由URB中dev_add和end_point两个字段指定的端点上去了。

接下来我们要接收device返回的执行INQUIRY命令的结果,这要启动一个输入事务,相对容易一些,只要填写URB就可以了,把transation_type=0x69,把end_point填上OUT endpoint的地址,本例中为2,buffer_off和buffer_seg指向缓冲区buffer,把buffer_length和actual_length均填为64,因为前面端点描述符表中写明包的最大长度为64,其它字段按常规填写,调用DOSUSB,在buffer中就可以得到返回的内容,按照SPC-3中对返回内容的解释即可了解设备的一些情况。

接收晚数据后,不要忘了接收CSW,方法也是启动一个输入事务,与接收数据完全相同,然后根据CSW的结构解释其含义。至此一个命令执行完毕。

范例

在本文的范例中,我们实现了如下内容:

  • 实现了Bulk-Only Mass Storage Reset

  • 实现了Get Max LUN

  • 实现了SCSI INQUIRY Command

  • 实现了SCSI READ CAPACITY (10) Command

  • 实现了SCSI REQUEST SENSE Command

  • 实现了SCSI TEST UNIT READY Command

  • 实现了SCSI READ (10) Command

最后的一个命令,我将从你的U盘上读出一个扇区。

最前面的两个命令,请翻阅《Universal Serial Bus Mass Storage Class - Bulk-Only Transport》第7页;INQUIRY、REQUEST SENSE、TEST UNIT READY三个命令请翻阅SPC-3的第142、221和232页;READ CAPACITY(10)和READ(10)命令,请翻阅SBC-2的第42和44页。

源代码请在下面网址下载:

http://blog.whowin.net/source/reader.rar

各种概念在前面已经介绍过了,程序无非就是实现这些概念,几乎所有的代码都是围绕着填写数据结构和显示返回结果的,所以代码本身并不难,更重要的是理解数据结构中个字段的含义,这可能不得不阅读一些规范,我想我不可能比规范说的更严谨更完整。

要注意的是,你使用的U盘不可能和我的完全一致,一般情况下有可能有变化的是:设备地址devAddr、输出端点地址outEndpoint和输入端点地址inEndpoint,所以在编译程序之前一定要仔细查看一下你的U盘的各种描述符表,如果这些值和我的U盘不同,请在主程序开始的地方,更改这几个变量。

另外,在主程序6th step中,scsiRead10(0),传递给scsiRead10的参数为0,含义是从LBA(Logical Block Address)为0的地方读取一个扇区,如果你向读取其它扇区,可以更改这个值,其最大值我们在实现 READ CAPACITY时已经读出了,可以参考。此外,注意CBW的字符顺序是little endian,所以我们在填写LBA和读取最大LBA时都做了相应的转换。

好了,应该没有什么了!

*本文转自网易博客,原文链接为:

http://hengch.blog.163.com/blog/static/10780067200851283245935/

万圣节活动,持续进行中~

验证开门密码

赢好礼

万圣节探险——惊魂一夜 | 留言有奖

↑↑点击上方链接,参与活动↑↑

11月8日,开奖揭秘答案

↓↓↓↓点击,查看更多新闻

命令 结构_只需一个命令!从你的U盘里读出更多内容相关推荐

  1. eps提取高程点在哪里_只需一个命令,就能提取CAD图纸所有高程点坐标,感觉学费白交了...

    原标题:只需一个命令,就能提取CAD图纸所有高程点坐标,感觉学费白交了 作为乙方设计师,经常会碰到一些甲方爸爸临时扔一个图纸过来,我们这个时候就要对图纸里提取有效数据信息才能进行下一步工作,拿到CAD ...

  2. linux实现命令解释器_想在Win10上安装Linux,只需一个命令即可实现

    尽管在Windows 10上,安装用于Linux 2的Windows子系统并不困难,但它需要很多步骤,如果你还想将WSL2设置为默认值,则需要更多步骤.但是,在将来的版本中,微软致力于简化安装过程,以 ...

  3. linux u盘fat32转ntfs,只需一个命令 FAT32格式磁盘无损转换成NTFSU盘

    临时来了个大文件要拷贝,容量超过4GB,但是U盘是FAT32格式的,拷贝不进去,尤其是U盘上还有好多文件,这种情况怎么办?有人说别考虑数据了,拷贝出来然后格成ExFAT就好了.不过ExFAT连Win ...

  4. python远程控制电脑_只需一个python脚本就可远程控制电脑,打开微信即可远程遥控操作...

    原标题:只需一个python脚本就可远程控制电脑,打开微信即可远程遥控操作 今天带给大家一个非常有意思的 python 程序,基于 itchat 实现微信控制电脑.你可以通过在微信发送命令,来拍摄当前 ...

  5. javaul材质包下载_只需一个水桶包 你就能装满时髦

    如果要列一份2015年春夏的it bag清单,水桶包必居其中.从一直断货的Mansur Gavriel到超智能的Ralph Lauren Ricky水桶包,无一不是明星潮人们的挚爱.麂皮.流苏70年代 ...

  6. win10禁用驱动程序强制签名_只需一个简单命令,在Win10上启用Windows恢复环境(WinRE)...

    在Windows10上,Windows Recovery Environment(Windows恢复环境,WinRE)是一项功能,它将自动修复.重置为出厂默认设置.系统映像恢复和其他故障排除工具捆绑在 ...

  7. 12层的bert参数量_只需一个损失函数、一个超参数即可压缩BERT,MSRA提出模型压缩新方法...

    来自武汉大学.北京航空航天大学和微软亚洲研究院的这项研究为模型压缩提供了新方向. 机器之心报道,参与:魔王. 论文链接:https://arxiv.org/pdf/2002.02925.pdf 这篇论 ...

  8. java venus_来认识一下venus-init——一个让你仅需一个命令开始Java开发的命令行工具...

    前言 不知道你是否有过这样的经历.不管你是什么岗位,前端也好,后端也罢,想去了解一下Java开发到底是什么样的,它是不是真的跟传说中的一样. 于是你拿起键盘,用触控板 ? '' : 抄起鼠标',开始了 ...

  9. deepin efi 启动u盘_如何安装Deepin国产操作系统?只需一个U盘就够了,超简单

    2019年快结束了,距离微软对Win7系统停止服务的日期也是越来越近了.与此同时,人们对国产操作系统的热度和期望值也是越来越高.在诸多国产操作系统中,Deepin系统是目前知名度最高的一个,而且无论在 ...

最新文章

  1. Qt 不再使用 LGPLv2.1 授权
  2. IOS开发笔记11-Object-C中的传递消息
  3. Oracle根据已有表的数据建立新表
  4. Python3 列表list合并的4种方法
  5. 爬虫之selenium和PhantomJS
  6. ERROR (ClientException): Unexpected API Error
  7. 每日一题(19)—— 用变量a给出下面的定义
  8. java+什么时候才需要deploy_细思极恐 - 什么才是真正的会写 Java ?
  9. mybais 之parameterType =list
  10. python中的numpy模块和pandas模块的区别_python的numpy模块- 01.pandas基本数据类型
  11. Centos 启用网卡出现 no link present. Check cable
  12. java rsa2加密算法_java RSA加密解密
  13. 科罗拉多州立大学计算机优势,科罗拉多州立大学——一所你可能不了解的好学校...
  14. 求助matlab崩溃问题的解决方案
  15. 基于react+antd的后台管理模板
  16. gephi mysql_用Gephi移动多个节点(Moving multiple nodes with Gephi)
  17. 加一度简答SEM竞价推广中遇到的4大难题
  18. 商品绑定可用的优惠券(多对多的绑定且一张优惠券只能使用于一个商品)
  19. Xmanager7 解决图形显示问题
  20. android酷炫转圈动画,android常用旋转线条加载动画

热门文章

  1. 95-190-741-源码-WindowFunction-窗口流简介
  2. 【Flink】Flink界面如何查看数据是否倾斜
  3. 【安全】JAAS/GSS-API/SASL/Kerberos简介
  4. Mac下gradle简介与安装
  5. 一个多线程练习,为什么直接运行和debug结果不一样
  6. 05-Prohibited package name: java异常原因
  7. 云计算实战系列十一(软件包管理)
  8. 奔跑吧兄弟变成机器人是哪一期_强竞技的《奔跑吧3》蜕变为“生活服务类综艺”,你还愿意看吗?...
  9. Bean复制的几种框架性能比较(BeanUtils、PropertyUtils、BeanCopier)
  10. 后端接口如何提高性能?从MySQL、ES、HBASE等技术一起探讨下!