開発者のタスク、またはベンダーなしでハンドヘルド スキャナーをフラッシュした方法

こんにちは。

私たち、Viktor Antipov と Ilya Aleshin は、今日は Python PyUSB を介して USB デバイスを操作した経験と、リバース エンジニアリングについて少しお話します。

開発者のタスク、またはベンダーなしでハンドヘルド スキャナーをフラッシュした方法

背景

2019年、ロシア連邦政府令第224号「識別手段によるタバコ製品のラベル表示に関する規則と、識別手段によるラベル表示義務の対象となる商品の流通を監視するための国家情報システムの導入機能の承認について」タバコ製品に関して」が施行されました。
この文書では、1年2019月XNUMX日から製造業者はタバコの各パックにラベルを付けることが義務付けられていると説明されています。 また、直接販売代理店は、Universal Transfer Document (UDD) を実行してこれらの製品を受け取る必要があります。 一方、店舗は、レジを通じてラベル付き製品の販売を登録する必要があります。

また、1年2020月XNUMX日よりラベルのないタバコ製品の流通が禁止されます。 これは、すべてのタバコのパックに特別な Datamatrix バーコードを付ける必要があることを意味します。 さらに、重要な点ですが、データマトリックスは通常ではなく、逆であることが判明しました。 つまり、白地に黒コードではなく、その逆です。

スキャナーをテストしたところ、ほとんどのスキャナーは再フラッシュ/再トレーニングする必要があることが判明しました。そうしないと、このバーコードを正常に動作させることができません。 私たちの会社は広大な領土に点在する多くの店舗を持っているため、この出来事の展開で私たちはひどい頭痛に見舞われることは確実でした。 数万のレジがあり、時間はほとんどありません。

何をすべきだったのでしょうか? 選択肢は XNUMX つあります。 まず、オンサイトのエンジニアが手動でスキャナを再フラッシュして調整します。 XNUMX 番目: 私たちはリモートで作業し、できれば XNUMX 回の反復で一度に多くのスキャナーをカバーします。

最初のオプションは、明らかに私たちには適していませんでした。エンジニアを訪問するのにお金を費やす必要があり、この場合、プロセスの制御と調整が困難になります。 しかし、最も重要なことは、人々が仕事をするということです。つまり、多くのエラーが発生する可能性があり、おそらく期限に間に合わない可能性があります。

XNUMX 番目のオプションは、XNUMX つの点ではないにせよ、誰にとっても良いことです。 一部のベンダーは、必要なすべてのオペレーティング システムに必要なリモート フラッシュ ツールを持っていませんでした。 そして締め切りが迫っていたので、自分の頭で考えなければなりませんでした。

次に、Debian 9.x OS 用のハンドヘルド スキャナ用ツールをどのように開発したかについて説明します (すべてのレジは Debian 上にあります)。

謎を解く: スキャナーをフラッシュする方法

ヴィクトル・アンティポフ氏が報じた。

ベンダーが提供する公式ユーティリティは Windows 上で動作し、IE でのみ動作します。 ユーティリティはスキャナーをフラッシュして構成できます。

ターゲット システムは Debian であるため、USB リダイレクタ サーバーを Debian にインストールし、USB リダイレクタ クライアントを Windows にインストールしました。 USB リダイレクタ ユーティリティを使用して、スキャナを Linux マシンから Windows マシンに転送しました。

Windows ベンダーのユーティリティはスキャナーを認識し、通常どおりフラッシュさえしました。 したがって、私たちは最初の結論を出しました。OS には何も依存せず、フラッシュ プロトコルの問題です。

わかりました。 Windows マシンでフラッシュを実行し、Linux マシンでダンプを削除しました。

私たちはダンプを WireShark に詰め込みましたが、...悲しくなりました (ダンプの詳細の一部は、興味がないので省略します)。

ダンプからわかったこと:

開発者のタスク、またはベンダーなしでハンドヘルド スキャナーをフラッシュした方法

開発者のタスク、またはベンダーなしでハンドヘルド スキャナーをフラッシュした方法

Wireshark によると、アドレス 0000 ~ 0030 は USB サービス情報です。

私たちは部品 0040 ~ 0070 に興味がありました。

XNUMX つの送信フレームからは、MOCFT 文字を除いて何も明らかではありませんでした。 これらの文字は、フレームの終わりまでの残りの文字と同様に、ファームウェア ファイルの文字であることが判明しました (ファームウェア ファイルが強調表示されています)。

開発者のタスク、またはベンダーなしでハンドヘルド スキャナーをフラッシュした方法

fd 3e 02 01 fe という記号が何を意味するのか、イリヤと同様に私も個人的には全く分かりませんでした。

次のフレームを確認しました (ここではサービス情報が削除され、ファームウェア ファイルが強調表示されています)。

開発者のタスク、またはベンダーなしでハンドヘルド スキャナーをフラッシュした方法

何が明らかになったのでしょうか? 最初の XNUMX バイトが何らかの定数であること。 後続のすべてのブロックでこれが確認されましたが、送信ブロックが終了する前に次のようになります。

開発者のタスク、またはベンダーなしでハンドヘルド スキャナーをフラッシュした方法

このフレームもまた、定数が変更され (強調表示され)、奇妙なことにファイルの一部があったため、驚異的でした。 ファイルの転送バイトのサイズは、1024 バイトが転送されたことを示しました。 またしても残りのバイトが何を意味するのか分かりませんでした。

まず、古いBBSのニックネームとして、標準的な伝送プロトコルを見直しました。 プロトコルなしで 1024 バイトが送信されました。 私はハードウェアの研究を開始し、1K Xmodem プロトコルに出会いました。 1024 バイトの送信が可能でしたが、注意点がありました。最初は 128 バイトのみで、エラーがなかった場合に限り、プロトコルは送信バイト数を増やしました。 すぐに 1024 バイトの転送が行われました。 私は伝送プロトコル、特に X モデムについて研究することにしました。

モデムには XNUMX つのバリエーションがありました。

まず、CRC8 サポートを備えた XMODEM パッケージ形式 (オリジナルの XMODEM):

開発者のタスク、またはベンダーなしでハンドヘルド スキャナーをフラッシュした方法

16 番目に、CRCXNUMX をサポートする XMODEM パケット フォーマット (XmodemCRC):

開発者のタスク、またはベンダーなしでハンドヘルド スキャナーをフラッシュした方法

SOH、パッケージ番号、CRC、パッケージ長を除いて、見た目は似ています。

1024 番目の送信ブロックの先頭を確認しました (もう一度ファームウェア ファイルを確認しましたが、すでに XNUMX バイトでインデントされていました)。

開発者のタスク、またはベンダーなしでハンドヘルド スキャナーをフラッシュした方法

おなじみのヘッダー fd 3e 02 が表示されましたが、次の 01 バイトはすでに変更されており、02 fe であったものが 02 fd になりました。 次に、1024 番目のブロックに 01 という番号が付けられていることに気づき、目の前に送信ブロックの番号が付けられていることがわかりました。 02 の最初のギアは 03、1 番目は 0、1 番目は 1 というようになります (ただし、もちろん 0 進数です)。 しかし、feからfdへの変更は何を意味するのでしょうか? 目は 02 ずつ減っていくのを見て、脳はプログラマは 02 から数えるのではなく 02 から数えることを思い出させました。では、なぜ最初のブロックは XNUMX ではなく XNUMX なのでしょうか? この質問に対する答えはまだ見つかりません。 しかし、XNUMX番目のブロックがどのようにカウントされるかは理解できました。 XNUMX 番目のブロックは、FF – (マイナス) の最初のブロックの番号にすぎません。 したがって、XNUMX 番目のブロックは = XNUMX (FF-XNUMX) = XNUMX FD と指定されました。 その後ダンプを読んだことで、私の推測が裏付けられました。

すると、次のような伝達の様子が浮かび上がってきました。

送信開始
fd 3e 02 – 開始
01 FE – 送信カウンタ
転送 (34 ブロック、1024 バイト転送)
fd 3e 1024 バイトのデータ (30 バイトのブロックに分割)。
送信終了
fd25

残りのデータは 1024 バイトに調整されます。

ブロック送信終了フレームは次のようになります。

開発者のタスク、またはベンダーなしでハンドヘルド スキャナーをフラッシュした方法

fd 25 – ブロック送信を終了する信号。 次の 2f 52 – サイズが最大 1024 バイトまでのファイルの残りの部分。 プロトコルから判断すると、2f 52 は 16 ビット CRC チェックサムです。

昔のことを思い出して、ファイルから 1024 バイトを取り出して 16 ビット CRC を計算するプログラムを C で作成しました。 プログラムを起動すると、これが 16 ビット CRC ではないことがわかりました。 再び昏迷が起こります - 約1024日間。 この間ずっと、チェックサムではないにしても、それが何であるかを理解しようとしていました。 英語のサイトを調べているときに、X モデムが独自のチェックサム計算、CRC-CCITT (XModem) を使用していることを発見しました。 この計算の C 実装は見つかりませんでしたが、このチェックサムをオンラインで計算するサイトを見つけました。 ファイルの XNUMX バイトを Web ページに転送すると、サイトはファイルのチェックサムと完全に一致するチェックサムを表示しました。

万歳! 最後の謎は解決しました。今度は独自のファームウェアを作成する必要がありました。 次に、強力なツールキット 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、コマンドを書き込むアドレス endpoint=03、応答を取得するアドレス endpoint=86。 残っているのは、コマンドを XNUMX 進数に変換することだけです。

開発者のタスク、またはベンダーなしでハンドヘルド スキャナーをフラッシュした方法

開発者のタスク、またはベンダーなしでハンドヘルド スキャナーをフラッシュした方法

デバイスはすでに見つかっているので、カーネルから切断しましょう...

開発者のタスク、またはベンダーなしでハンドヘルド スキャナーをフラッシュした方法

...そしてアドレス 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 にあります。

開発者のタスク、またはベンダーなしでハンドヘルド スキャナーをフラッシュした方法

開発者のタスク、またはベンダーなしでハンドヘルド スキャナーをフラッシュした方法

開発者のタスク、またはベンダーなしでハンドヘルド スキャナーをフラッシュした方法

素晴らしい! システム バーコードを XNUMX 進数に変換します。 以上で、トレーニング機能の準備が整いました。

ファームウェアについてはどうですか? すべて同じに見えますが、ニュアンスがあります。

フラッシュ プロセスの完全なダンプを取得したので、私たちは何を扱っているのかを大まかに理解しました。 XMODEM に関する記事は次のとおりです。一般的な用語ではありますが、この通信がどのように行われるかを理解するのに非常に役立ちました。 http://microsin.net/adminstuff/others/xmodem-protocol-overview.html 読むことをお勧めします。

ダンプを見ると、フレーム サイズが 1024、URB データ サイズが 64 であることがわかります。

開発者のタスク、またはベンダーなしでハンドヘルド スキャナーをフラッシュした方法

したがって – 1024/64 – ブロック内に 16 行を取得し、ファームウェア ファイルを一度に 1 文字ずつ読み取り、ブロックを形成します。 ブロック内の 1 行を特殊文字 fd3e02 + ブロック番号で補完します。
次の 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 フラッシュ モードのスキャナは XNUMX 進数を受け入れません。 したがって、各行を 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