La tâche du développeur ou comment nous avons numérisé des scanners manuels sans fournisseur

Bonjour à tous.

Nous, Victor Antipov et Ilya Aleshin, parlerons aujourd'hui de notre expérience avec les périphériques USB via Python PyUSB et un peu de rétro-ingénierie.



Contexte


En 2019, le décret n ° 224 du gouvernement de la Fédération de Russie sur l'approbation des règles d'étiquetage des produits du tabac au moyen de l'identification et les modalités de mise en place d'un système national d'information pour le contrôle de la circulation des marchandises soumises à l'étiquetage obligatoire au moyen de l'identification des produits du tabac est entré en vigueur.
Le document explique qu'à partir du 1er juillet 2019, les fabricants sont tenus d'étiqueter chaque paquet de tabac. Et les distributeurs directs devraient recevoir ces produits avec la conception d'un document de transfert universel (UPD). Les magasins, à leur tour, doivent enregistrer la vente de produits étiquetés via la caisse enregistreuse.

De plus, à partir du 1er juillet 2020, les produits du tabac non marqués sont interdits. Cela signifie que tous les paquets de cigarettes doivent être étiquetés avec un code-barres Datamatrix spécial. Et - un point important - il s'est avéré que Datamatrix ne sera pas ordinaire, mais inverse. Ce n'est pas un code noir sur blanc, mais vice versa.

Nous avons testé nos scanners, et il s'est avéré que la plupart d'entre eux doivent être reflashés / recyclés, sinon ils ne peuvent tout simplement pas fonctionner normalement avec ce code-barres. Cette tournure des événements nous a garanti un mal de tête sévère, car notre entreprise compte de nombreux magasins disséminés sur un vaste territoire. Plusieurs dizaines de milliers de caisses - et extrêmement peu de temps.

Que fallait-il faire? Il y a deux options. Premièrement: les ingénieurs de l'installation reflasher et ajuster manuellement les scanners. Deuxièmement: nous travaillons à distance et, de préférence, nous couvrons plusieurs scanners en une seule fois.

La première option, évidemment, ne nous convenait pas: il faudrait dépenser de l'argent pour des visites d'ingénieurs sur le terrain, et il est difficile de contrôler et de coordonner le processus dans ce cas. Mais la chose la plus importante est que les gens travailleraient, c'est-à-dire que potentiellement nous aurions beaucoup d'erreurs et, très probablement, ne respecterions pas la date limite.

La deuxième option est bonne pour tout le monde, sinon pour un mais. Certains fournisseurs ne disposaient pas des outils de flash à distance dont nous avions besoin pour tous les systèmes d'exploitation requis. Et comme les délais étaient serrés, j'ai dû réfléchir de ma propre tête.

Ensuite, nous décrirons comment nous avons développé des outils pour les scanners portables pour le système d'exploitation Debian 9.x (nous avons tout le box-office Debian).

:


Dit Victor Antipov.

L'utilitaire officiel fourni par le vendeur fonctionne sous Windows, et uniquement avec IE. L'utilitaire peut flasher et configurer le scanner.

Comme le système cible est Debian, nous avons installé le serveur de redirection USB sur le serveur Debian et le client de redirection USB sur Windows. À l'aide des utilitaires de redirection USB, le scanner a été transféré de la machine Linux à la machine Windows.

L'utilitaire du fournisseur Windows a vu le scanner et l'a même fait clignoter normalement. Ainsi, la première conclusion a été tirée: rien ne dépend de l'OS, l'affaire est dans le protocole clignotant.

D'ACCORD. Un clignotement a été lancé sur une machine Windows et un vidage a été supprimé sur une machine Linux.

Ils ont bourré le vidage dans WireShark et ... étaient tristes (je vais omettre une partie des détails du vidage, ils ne sont d'aucun intérêt).

Quel vidage nous a montré:





Les adresses 0000-0030, à en juger par Wireshark, sont des informations de service USB.

Nous étions intéressés par la pièce 0040-0070.

Rien n'était clair à partir d'une trame de transmission, à l'exception des caractères MOCFT. Ces symboles se sont avérés être des symboles du fichier du firmware, ainsi que du reste des symboles jusqu'à la fin du cadre (le fichier du firmware est mis en surbrillance):



que signifiaient les symboles fd 3e 02 01 fe, personnellement, comme Ilya, je n'en avais aucune idée.

J'ai regardé le cadre suivant (les informations de service sont supprimées ici, le fichier du firmware est mis en surbrillance):



Qu'est - ce qui est devenu clair? Que les deux premiers octets sont une sorte de constante. Tous les blocs suivants l'ont confirmé, mais avant la fin du bloc de transmission:



Ce cadre est également entré dans une stupeur, car la constante a changé (mise en évidence) et, curieusement, il y avait une partie du fichier. La taille des octets transmis du fichier indique que 1024 octets ont été transférés. Ce que les octets restants signifiaient - je ne savais pas encore.

Tout d'abord, comme un ancien surnom BBS, j'ai révisé les protocoles de transfert standard. 1024 octets, aucun protocole transmis. Il a commencé à étudier le matériel et est tombé sur le protocole 1K Xmodem. Il a permis de transmettre 1024, mais avec une nuance: au début seulement 128, et seulement en l'absence d'erreurs le protocole a augmenté le nombre d'octets transmis. J'ai immédiatement eu une transmission de 1024 octets. J'ai décidé d'étudier les protocoles de transmission, et plus particulièrement le X-modem.

Il y avait deux variantes du modem.

Tout d'abord, le format du package XMODEM avec prise en charge CRC8 (XMODEM d'origine):



Deuxièmement, le format de paquet XMODEM avec prise en charge CRC16 (XmodemCRC):



il semble similaire, à l'exception de SOH, du numéro de paquet et du CRC et de la longueur du paquet.

J'ai regardé le début du deuxième bloc de transmission (et j'ai encore vu le fichier du firmware, mais avec un retrait de 1024 octets):



J'ai vu l'en-tête familier fd 3e 02, mais les deux octets suivants ont déjà changé: c'était 01 fe, et il est devenu 02 fd. Puis j'ai remarqué que le deuxième bloc est maintenant numéroté 02 et ainsi compris: devant moi se trouve la numérotation du bloc de transmission. La première transmission 1024 est 01, la deuxième 02, la troisième 03 et ainsi de suite (mais en hexadécimal, bien sûr). Mais que signifie le passage de fe à fd? Les yeux ont vu une diminution de 1, le cerveau a rappelé que les programmeurs comptent à partir de 0, pas à partir de 1. Mais alors pourquoi le premier bloc 1, pas 0? Je n'ai pas trouvé la réponse à cette question. Mais j'ai compris comment le deuxième bloc est considéré. Le deuxième bloc n'est rien de plus que FF - (moins) le numéro du premier bloc. Ainsi, le deuxième bloc a été désigné comme = 02 (FF-02) = 02 FD. La lecture ultérieure de la décharge a confirmé mon intuition.

Puis l'image suivante du programme a commencé à émerger:

Le début du programme
fd 3e 02 - Début
01 FE - compteur de transmission
Transmission (34 blocs, 1024 octets sont transmis)
fd 3e 1024 octets de données (divisés en blocs de 30 octets).
Fin de transmission
fd 25

Reste de données à aligner sur 1024 octets.

À quoi ressemble la trame de fin de transfert de bloc:



fd 25 - signal à la fin du transfert de bloc. 2f 52 suivant - les restes du fichier jusqu'à 1024 octets. 2f 52, à en juger par le protocole, est une somme de contrôle CRC de 16 bits.

Sur la base de l'ancienne mémoire, j'ai créé un programme en C qui a extrait 1024 octets d'un fichier et lu le CRC 16 bits. Le lancement du programme a montré qu'il ne s'agit pas d'un CRC 16 bits. Encore une fois stupeur - pendant environ trois jours. Pendant tout ce temps, j'ai essayé de comprendre ce que cela pouvait être sinon une somme de contrôle. En étudiant des sites en anglais, j'ai découvert que le X-modem utilise son propre calcul de somme de contrôle - CRC-CCITT (XModem). Je n'ai trouvé aucune implémentation en C pour ce calcul, mais j'ai trouvé un site qui lisait cette somme de contrôle en ligne. En téléchargeant 1024 octets de mon fichier sur la page Web, le site m'a montré une somme de contrôle qui coïncidait complètement avec la somme de contrôle du fichier.

Hourra! La dernière énigme est résolue, maintenant vous deviez créer votre propre firmware. Ensuite, j'ai transféré mes connaissances (et elles ne sont restées que dans ma tête) à Ilya, qui est familière avec des outils puissants - Python.

Création de programme


Rapporté par Ilya Aleshin.

Ayant reçu les instructions appropriées, j'étais très «heureux».

Où commencer? D'accord, depuis le début.  Du dumping à partir d'un port USB.

Exécutez USB-pcap https://desowin.org/usbpcap/tour.html

Choisissez le port auquel l'appareil est connecté et le fichier où nous enregistrerons le vidage.



Nous connectons le scanner à la machine sur laquelle le logiciel natif EZConfigScanning pour Windows est installé.



On y trouve l'intérêt d'envoyer des commandes à l'appareil. Mais qu'en est-il des équipes? Où les obtenir?
Lorsque le programme démarre, l'équipement est interrogé automatiquement (nous le verrons un peu plus tard). Et il y avait des codes à barres de formation à partir de documents d'équipement officiels. DEFALT. Voici notre équipe.



Les données nécessaires sont reçues. Ouvrez dump.pcap via Wireshark.

Bloquer au démarrage EZConfigScanning. Les points rouges sont des endroits à surveiller.





Voyant tout cela pour la première fois, j'ai perdu courage. Où creuser plus loin n'est pas clair.

Un peu de brainstorming et-et-et ... Aha! Dans une décharge publique , en est dans , et en est hors .

Recherche sur Google ce qu'est URB_INTERRUPT. J'ai découvert qu'il s'agit d'une méthode de transfert de données. Et il existe 4 méthodes de ce type: contrôle, interruption, isochrone, en vrac. Vous pouvez les lire séparément.

Et les adresses des points de terminaison dans l'interface du périphérique USB peuvent être obtenues soit via la commande «lsusb –v», soit au moyen de pyusb.

Vous devez maintenant trouver tous les appareils avec ce VID. Vous pouvez rechercher spécifiquement par VID: PID.



Cela ressemble à ceci:





Nous avons donc les informations nécessaires: les commandes P_INFO. ou DEFALT, indique où écrire les commandes endpoint = 03 et où obtenir la réponse endpoint = 86. Il ne reste plus qu'à traduire les commandes en hexadécimal.





Puisque nous avons déjà trouvé le périphérique, déconnectez-le du noyau ...



... et écrivez au point de terminaison avec l'adresse 0x03,



... puis lisez la réponse du point de terminaison avec l'adresse 0x86.



Réponse structurée:

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

Nous voyons ces données dans dump.pcap.







Bien! Nous traduisons les codes-barres du système en hexadécimal. Tout, la fonctionnalité de formation est prête.

Que faire avec le firmware? Il semble que tout soit pareil, mais il y a une nuance.

Après avoir supprimé un vidage complet du processus de clignotement, nous avons à peu près compris à quoi nous faisions face. Voici un article sur XMODEM qui a vraiment aidé à comprendre comment cette communication se produit, bien qu'en termes généraux: http://microsin.net/adminstuff/others/xmodem-protocol-overview.html Je recommande de le lire.

En regardant dans le vidage, vous pouvez voir que la taille de la trame est 1024 et la taille des données URB est 64.



Par conséquent, 1024/64, nous obtenons 16 lignes dans un bloc, lisons le fichier du firmware par 1 caractère et formons un bloc. Compléter 1 ligne dans un bloc avec des caractères spéciaux fd3e02 + numéro de bloc.
Les 14 lignes suivantes sont complétées par fd25 +, en utilisant XMODEM.calc_crc (), nous calculons la somme de contrôle de l'ensemble du bloc (il a fallu beaucoup de temps pour comprendre que «FF - 1» est CSUM) et la dernière 16e ligne est complétée par fd3e.

Il semblerait que tout, lisez le fichier du firmware, frappez les blocs, déconnectez le scanner du noyau et envoyez-le à l'appareil. Mais pas si simple. Le scanner doit être mis en mode firmware en lui
envoyant NEWAPP = '\\ xfd \\ x0a \\ x16 \\ x4e \\ x2c \\ x4e \\ x45 \\ x57 \\ x41 \\ x50 \\ x50 \\ x0d'.
D'où vient cette commande ?? De la décharge.



Mais nous ne pouvons pas envoyer le bloc entier au scanner en raison de la restriction de 64:



Eh bien, le scanner en mode clignotant NEWAPP n'accepte pas non plus l'hex. Il est donc nécessaire de traduire chaque ligne 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]

Et envoyez déjà ces données au scanner.

Nous obtenons la réponse:

[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]

Si vous consultez l'article sur XMODEM, cela devient clair: les données ont été acceptées.



Une fois tous les blocs transférés, nous terminons le transfert END_TRANSFER = '\ xfd \ x01 \ x04'.

Eh bien, puisque ces blocs ne contiennent aucune information pour les gens ordinaires, nous ferons le firmware en mode caché par défaut. Et juste au cas où, via tqdm, nous organiserons une barre de progression.



En fait, le reste est petit. Il ne reste plus qu'à encapsuler la solution dans des scripts de réplication de masse à une heure clairement définie, afin de ne pas ralentir le processus de travail au box-office, et ajouter la journalisation.

Total


Après avoir passé beaucoup de temps et d'énergie et de cheveux sur la tête , nous avons pu développer les solutions dont nous avions besoin, de plus, nous avons respecté le délai. Dans le même temps, les scanners sont reflashés et recyclés maintenant de manière centralisée, nous contrôlons clairement l'ensemble du processus. L'entreprise a économisé du temps et de l'argent et nous avons acquis une expérience inestimable dans la rétro-ingénierie de ce type d'équipement.

All Articles