开发人员的任务,或者我们如何在没有供应商的情况下扫描手持式扫描仪

大家好。

我们,Victor Antipov和Ilya Aleshin,今天将谈论我们通过Python PyUSB在USB设备方面的经验以及一些有关逆向工程的知识。



背景


2019年,俄罗斯联邦政府第224号法令``关于批准通过识别手段对烟草制品进行标记的规则,以及引入国家信息系统以监控与烟草制品有关的通过强制手段进行识别的强制性标签的商品流通的细节''的法令。
该文件解释说,自2019年7月1日起,制造商必须在每包烟草上贴标签。直接分销商应在收到带有通用转让文件(UPD)设计的产品时使用这些产品。反过来,商店需要通过收银机来注册带有标签的产品的销售。

另外,从2020年7月1日起,禁止使用未标记的烟草制品。这意味着所有香烟包装都必须带有特殊的Datamatrix条形码标签。而且-重要的一点-事实证明,Datamatrix不会是普通的,而是相反的。也就是说,不是黑码是白色,反之亦然。

我们测试了扫描仪,结果发现大多数扫描仪都需要刷​​新/重新培训,否则它们将无法正常使用此条形码。这一轮事件使我们感到非常头疼,因为我们公司的商店很多,分布在广阔的地区。数万个收银台-很少的时间。

该怎么办?有两种选择。首先:工厂的工程师手动重新刷新并调整扫描仪。第二:我们远程工作,最好一次迭代一次覆盖许多扫描仪。

显然,第一种选择不适合我们:我们将不得不花钱在工程师的实地考察上,在这种情况下很难控制和协调流程。但是最重​​要的是,人们会努力工作,也就是说,潜在地我们会犯很多错误,并且很可能无法按时完成任务。

第二种选择对每个人都有好处,即使不是。一些供应商没有所有必需的操作系统所需的远程刷新工具。而且由于截止日期快要用完了,我不得不自己动手思考。

接下来,我们将描述如何为Debian 9.x OS(我们拥有所有Debian票房)开发用于手持式扫描仪的工具。

:


维克多·安蒂波夫(Victor Antipov)说。

供应商提供的官方实用程序可在Windows下运行,并且仅适用于IE。该实用程序可以闪烁并配置扫描仪。

由于目标系统是Debian,因此我们在Debian服务器上安装了usb-redirector服务器,在Windows上安装了usb-redirector客户端。使用usb-redirector实用程序,将扫描程序从Linux计算机转发到Windows计算机。

Windows供应商提供的实用程序可以看到扫描仪,甚至可以正常闪烁。因此,得出了第一个结论:什么都不依赖于OS,问题在于闪存协议。

好。在Windows计算机上启动了刷新,在Linux计算机上删除了转储。

他们将垃圾堆塞入WireShark,并且...很难过(我将省略部分垃圾堆详细信息,它们没有兴趣)。

什么转储显示给我们:





通过Wireshark判断,地址0000-0030是USB服务信息。

我们对零件0040-0070感兴趣。

除了MOCFT字符外,从一个传输帧中看不到任何东西。这些符号原来是固件文件中的符号,以及其余的符号,直到帧结束为止(突出显示了固件文件):



fd 3e 02 01 fe这些符号什么意思,我个人像Ilya一样也不知道。

我看着下一帧(此处删除了服务信息,突出显示了固件文件):



清楚的什么?前两个字节是某种常数。所有后续块都确认了这一点,但在传输块结束之前:



由于常量已更改(突出显示),而且奇怪的是文件的一部分,因此该帧也陷入了混乱。文件传输字节的大小指示已传输1024个字节。剩余字节意味着什么-我再也不知道。

首先,像旧的BBS昵称一样,我修改了标准传输协议。 1024字节,未通过任何协议。他开始研究材料,偶然发现了1K Xmodem协议。它允许发送1024,但有一个细微差别:最初只有128,并且只有在没有错误的情况下,协议才会增加发送的字节数。我立即传输了1024个字节。我决定研究传输协议,特别是X调制解调器。

调制解调器有两种变体。

首先,具有CRC8支持的XMODEM软件包的格式(原始XMODEM):



其次,具有CRC16支持的XMODEM数据包格式(XmodemCRC):



看起来很相似,除了SOH,数据包编号和CRC以及数据包的长度。

我查看了第二个传输块的开头(再次看到了固件文件,但缩进了1024字节):



我看到了熟悉的标头fd 3e 02,但接下来的两个字节已经更改:它是01 fe,并变为02 fd。然后我注意到第二个块现在编号为02,因此可以理解:在我前面是传输块的编号。第一个1024传输为01,第二个为02,第三个为03,依此类推(当然,以十六进制表示)。但是从fe到fd的变化意味着什么?眼睛看到减少了1,大脑提醒程序员从0开始计数,而不是从1开始计数。但是为什么第一个块1不是0?我没有找到这个问题的答案。但我知道如何考虑第二个障碍。第二个块只不过是FF-(减)第一个块的编号。因此,第二个块被指定为= 02(FF-02)= 02 FD。随后对垃圾场的阅读证实了我的直觉。

然后该程序的以下图片开始出现:

程序的开始
fd 3e 02-开始
01 FE-传输计数器
传输(34个块,发送1024个字节)
fd 3e 1024个字节的数据(分为30个字节的块)。
传输结束
fd 25

剩余数据,以对齐1024字节。

块传输结束帧是什么样的:



fd 25-信号到块传输结束。下一个2f 52-文件的剩余部分,最多1024个字节。根据协议判断,2f 52是一个16位CRC校验和。

基于旧内存,我用C语言编写了一个程序,该程序从文件中提取1024个字节并读取16位CRC。该程序的启动表明这不是16位CRC。再次昏昏欲睡-大约三天。一直以来,我一直试图了解如果不是校验和,那会是什么。在研究英语站点时,我发现X调制解调器使用自己的校验和计算-CRC-CCITT(XModem)。我没有找到用C进行此计算的任何实现,但是我找到了一个在线读取此校验和的站点。通过将我的文件的1024个字节上传到网页,该网站向我显示了一个校验和,该校验和与文件中的校验和完全一致。

万岁!解决了最后一个难题,现在您必须制作自己的固件。然后,我将我的知识(并且只保留在我的脑海中)转移给了熟悉强大工具Python的Ilya。

程序制作


由Ilya Aleshin讲述。

收到适当的指示后,我感到非常“高兴”。

从哪里开始?对,从一开始。从USB端口转储。

运行USB-pcap https://desowin.org/usbpcap/tour.html

选择设备连接到的端口以及用于保存转储的文件。



我们将扫描仪连接到安装了适用于Windows的本机EZConfigScanning软件的计算机。



在其中,我们找到了向设备发送命令的要点。但是球队呢?在哪里买?
程序启动时,将自动询问设备(我们稍后会看到)。官方设备文档中有培训条形码。缺货。这是我们的团队。



接收到必要的数据。通过wireshark打开dump.pcap。

在启动EZConfigScanning时阻止。红点是要注意的地方。





第一次看到这一切,我很沮丧。在哪里进一步挖掘还不清楚。

有点脑力激荡和……然后啊!在转储,,而且

谷歌搜索什么是URB_INTERRUPT。发现这是一种数据传输方法。共有4种方法:控制,中断,同步,批量。您可以分别阅读它们。

USB设备接口中的端点地址可以通过“ lsusb –v”命令或通过pyusb获得。

现在,您需要查找具有该VID的所有设备。您可以通过VID:PID专门搜索。



看起来像这样:





因此,我们有了必要的信息:P_INFO命令。或DEFALT,指出在哪里写命令端点= 03,在哪里获得答案端点= 86。仅保留以十六进制转换命令。





因为我们已经找到了设备,所以将其与内核断开连接



……并写入地址为0x03的端点,



然后从地址为0x86的端点读取响应。



结构化答案:

P_INFOfmt: 1
mode: app
app-present: 1
boot-present: 1
hw-sn: 18072B44CA
hw-rev: 0x20
cbl: 4
app-sw-rev: CP000116BBA
boot-sw-rev: CP000014BAD
flash: 3
app-m_name: Voyager 1450g
boot-m_name: Voyager 1450g
app-p_name: 1450g
boot-p_name: 1450g
boot-time: 16:56:02
boot-date: Oct 16 2014
app-time: 08:49:30
app-date: Mar 25 2019
app-compat: 289
boot-compat: 288
csum: 0x6986

我们在dump.pcap中看到此数据。







精细!我们将系统条形码转换为十六进制。一切就绪,培训功能已准备就绪。

固件怎么办?看起来一切都一样,但有细微差别。

删除了闪存过程的完整转储后,我们大致了解了我们正在处理的内容。以下是有关XMODEM的文章,尽管总的来说,它确实有助于理解这种通信的发生方式:http : //microsin.net/adminstuff/others/xmodem-protocol-overview.html我建议您阅读它。

在转储中查看,您可以看到帧大小为1024,而URB数据的大小为64。



因此,在1024/64中,我们在一个块中获得16行,以1个字符读取固件文件并形成一个块。在块中用特殊字符fd3e02 +块号补充1行。
接下来的14行用fd25 +补充,使用XMODEM.calc_crc()我们计算整个块的校验和(花了很多时间才知道“ FF-1”是CSUM),而最后16行用fd3e补充了。

看起来,一切都已读完固件文件,敲中了块,将扫描仪与内核断开连接并将其发送至设备。但不是那么简单。扫描仪必须通过
发送NEWAPP ='\\ xfd \\ x0a \\ x16 \\ x4e \\ x2c \\ x4e \\ x45 \\ x57 \\ x41 \\ x50 \\ x50 \\ x0d' 进入固件模式
这个命令从哪里来?从转储。



但是由于64的限制,我们无法将整个块发送给扫描仪:



嗯,处于NEWAPP闪烁模式的扫描仪也不接受十六进制。因此,有必要翻译每一行bytes_array

[253, 10, 22, 78, 44, 78, 69, 87, 65, 80, 80, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

并且已经将该数据发送到扫描仪。

我们得到答案:

[2, 1, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

如果查看有关XMODEM的文章,则很清楚:数据已被接受。



传输完所有块后,我们完成传输END_TRANSFER ='\ xfd \ x01 \ x04'。

好吧,由于这些存储区对普通人没有任何信息,因此默认情况下,我们会将固件设置为隐藏模式。以防万一,通过tqdm,我们将组织一个进度条。



实际上,其余的很小。剩下的只是将解决方案包装在脚本中,以便在明确定义的时间进行大规模复制,以免减慢票房的工作过程并增加日志记录。


头上花费了大量时间,精力和头发之后,我们能够开发所需的解决方案,并且能够按时完成任务。同时,现在对扫描仪进行重新刷新和集中培训,我们可以清楚地控制整个过程。该公司节省了时间和金钱,并且我们在此类设备的逆向工程方面获得了宝贵的经验。

All Articles