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

您好!

我们,Viktor Antipov 和 Ilya Aleshin,今天将讨论我们通过 Python PyUSB 使用 USB 设备的经验以及一些关于逆向工程的知识。

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

史前

2019年,俄罗斯联邦政府第224号法令“关于批准带有识别手段的烟草制品标签规则和实施国家信息系统的特征,以监控强制带有识别手段的标签的商品流通”与烟草制品有关的规定”生效。
该文件解释说,从 1 年 2019 月 XNUMX 日起,制造商必须在每包烟草上贴上标签。 直接分销商必须通过执行通用转移文件 (UDD) 来接收这些产品。 反过来,商店需要通过收银机登记贴有标签的产品的销售情况。

此外,自1年2020月XNUMX日起,禁止无标签烟草制品流通。 这意味着所有香烟包装都必须标有特殊的 Datamatrix 条形码。 此外 - 重要的一点 - 事实证明,数据矩阵不会是普通的,而是逆的。 也就是说,不是白底黑字,而是反之亦然。

我们测试了我们的扫描仪,结果发现大多数扫描仪都需要重新刷新/重新训练,否则它们根本无法正常使用此条形码。 这一变故让我们非常头疼,因为我们公司有很多商店,而且分布在很大的地区。 数以万计的收银机——而且时间很少。

该怎么办? 有两种选择。 第一:现场工程师手动重新闪光并调整扫描仪。 第二:我们远程工作,并且最好在一次迭代中同时覆盖许多扫描仪。

显然,第一种选择不适合我们:我们必须花钱请工程师,而且在这种情况下很难控制和协调流程。 但最重要的是人们会工作,也就是说,我们可能会遇到很多错误,而且很可能无法按时完成任务。

第二种选择对每个人都有好处,即使不是一件事。 一些供应商没有我们所需的所有操作系统所需的远程刷新工具。 由于最后期限已到,我不得不用自己的头脑思考。

接下来,我们将告诉您我们如何为 Debian 9.x 操作系统(我们所有的收银机都在 Debian 上)开发手持式扫描仪工具。

解决谜题:如何闪烁扫描仪

维克多·安蒂波夫报道。

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

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

Windows 供应商的实用程序看到了扫描仪,甚至可以正常刷新它。 因此,我们得出第一个结论:与操作系统无关,而是闪存协议的问题。

好的。 我们在 Windows 计算机上运行刷新,并在 Linux 计算机上删除转储。

我们将转储数据塞入 WireShark 中,然后……感到难过(我将省略转储数据的一些细节,它们没有任何意义)。

转储向我们展示了什么:

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

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

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

我们对 0040-0070 部分感兴趣。

除了 MOCFT 字符之外,一帧传输帧中没有任何内容是清晰的。 这些字符原来是固件文件中的字符,以及直到帧末尾的剩余字符(固件文件突出显示):

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

我个人和伊利亚一样,不知道符号 fd 3e 02 01 fe 的含义。

我查看了以下框架(此处已删除服务信息,固件文件已突出显示):

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

什么变得清楚了? 前两个字节是某种常量。 所有后续块都证实了这一点,但在传输块结束之前:

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

这个框架也令人震惊,因为常数已经改变(突出显示),而且奇怪的是,还有文件的一部分。 文件传输字节大小显示传输了1024字节。 我再次不知道剩余字节意味着什么。

首先,作为一个老BBS昵称,回顾一下标准传输协议。 没有协议传输 1024 字节。 我开始研究硬件并遇到了 1K Xmodem 协议。 它允许传输 1024,但有一个警告:最初只有 128,并且只有在没有错误的情况下,协议才会增加传输的字节数。 我立即传输了 1024 字节。 我决定研究传输协议,特别是 X-modem。

调制解调器有两种变体。

一、支持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-modem 使用自己的校验和计算 - CRC-CCITT (XModem)。 我没有找到任何此计算的 C 实现,但我找到了一个在线计算此校验和的站点。 将我的文件的 1024 字节传输到网页后,该网站向我显示了一个与文件中的校验和完全匹配的校验和。

万岁! 最后一个谜题解决了,现在我需要制作自己的固件。 接下来,我将我的知识(它只留在我的脑海中)传授给熟悉强大的 Python 工具包的 Ilya。

创建程序

伊利亚·阿莱辛报道。

收到相应的指示后,我非常“高兴”。

从哪里开始? 没错,从一开始就是这样。  从 USB 端口获取转储。

启动 USB-pcap https://desowin.org/usbpcap/tour.html

选择设备连接的端口以及我们将保存转储的文件。

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

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

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

在其中我们找到用于向设备发送命令的项目。 但团队呢? 我在哪里可以得到它们?
当程序启动时,设备会自动轮询(我们稍后会看到)。 还有来自官方设备文件的培训条形码。 默认。 这是我们的团队。

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

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

启动 EZConfigScanning 时阻止。 需要注意的地方都用红色标注了。

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

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

第一次看到这一切,我心灰意冷。 目前尚不清楚下一步该在哪里挖掘。

集思广益,然后……啊哈! 在垃圾场里 输出 - inin输出.

我用谷歌搜索了 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 个字符并形成一个块。 用特殊字符 fd1e3 + 块号补充块中的 02 行。
接下来的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 = 'xfdx01x04'。

好吧,由于这些块对于普通人来说不携带任何信息,因此我们将默认以隐藏模式安装固件。 为了以防万一,我们将通过 tqdm 组织一个进度条。

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

其实,这都是一些小事。 剩下的就是将解决方案包装在脚本中,以便在明确定义的时间进行大规模复制,以免减慢结帐工作的过程,并添加日志记录。

经过花费了大量的时间和精力,我们终于能够开发出我们需要的解决方案,并且也按时完成了任务。 同时,扫描仪现在已集中重新闪光和重新训练,我们清楚地控制整个过程。 该公司节省了时间和金钱,并且我们在此类逆向工程设备方面获得了宝贵的经验。

来源: habr.com

添加评论