Ahoj všichni
Dnes se s námi Viktor Antipov a Ilja Alešin podělí o své zkušenosti s prací s USB zařízeními přes Python PyUSB a něco málo o reverzním inženýrství.

pravěk
V roce 2019 vstoupilo v platnost usnesení ruské vlády č. 224 „O schválení Pravidel pro označování tabákových výrobků identifikačními prostředky a specifikace zavádění státního informačního systému pro sledování oběhu zboží podléhajícího povinnému označování identifikačními prostředky ve vztahu k tabákovým výrobkům“.
Dokument vysvětluje, že od 1. července 2019 jsou výrobci povinni označovat každý balíček tabáku. Přímí distributoři musí tyto výrobky obdržet s univerzálním přepravním dokladem (UTD). Obchody zase musí prodej označených výrobků zaregistrovat u pokladny.
Také od 1. července 2020 je zakázán oběh neoznačených tabákových výrobků. To znamená, že všechny krabičky cigaret musí být označeny speciálním čárovým kódem Datamatrix. Důležité je, že bylo odhaleno, že Datamatrix nebude standardní čárový kód, ale inverzní. Tedy ne černý kód na bílém, ale opačný.
Otestovali jsme naše skenery a ukázalo se, že většina z nich potřebuje přeprogramovat nebo přeškolit, jinak by s tímto čárovým kódem jednoduše nefungovaly správně. Tento zvrat událostí nám zaručil velké problémy, protože naše společnost má obrovské množství prodejen roztroušených po rozlehlém území. Desítky tisíc pokladen – a jen velmi málo času.
Co bychom měli dělat? Existují dvě možnosti. Zaprvé, technici na místě ručně přeprogramují a doladí skenery. Zadruhé, pracujeme na dálku a ideálně pokryjeme více skenerů v jedné iteraci.
První možnost pro nás očividně nebyla vhodná: museli bychom vynaložit peníze na návštěvy techniků a bylo by obtížné celý proces monitorovat a koordinovat. Ale co je nejdůležitější, zapojilo by to lidi, což znamenalo, že bychom se potenciálně setkali s řadou chyb a pravděpodobně bychom promeškali termín.
Druhá možnost by byla perfektní, až na jeden problém. Někteří dodavatelé neměli nástroje pro vzdálené flashování, které jsme potřebovali pro všechny požadované operační systémy. A protože termíny byly krátké, museli jsme to vyřešit sami.
Dále si povíme, jak jsme vyvinuli nástroje pro ruční skenery s operačním systémem Debian 9.x (všechny naše pokladny probíhají na Debianu).
Vyřešte hádanku: jak blikat skener
Viktor Antipov vypráví příběh.
Oficiální utilita od dodavatele funguje ve Windows, ale pouze s Internet Explorerem. Umí firmware flashovat a konfigurovat skener.
Protože naším cílovým systémem je Debian, nainstalovali jsme na Debian server USB-redirector a na Windows klienta USB-redirector. Pomocí utilit USB-redirector jsme přesměrovali skener z počítače se systémem Linux na počítač se systémem Windows.
Utilita pro Windows od dodavatele skener detekovala a dokonce ho úspěšně naprogramovala. Náš první závěr tedy: operační systém s tím nemá nic společného; problém spočívá v protokolu pro naprogramování flashování.
OK. Spustili jsme aktualizaci firmwaru na počítači s Windows a na počítači s Linuxem jsme provedli výpis dat.
Nahráli jsme dump do WireSharku a... bylo nám z toho smutno (některé detaily dumpu přeskočím, nejsou pro mě zajímavé).
Co nám skládka ukázala:


Adresy 0000-0030 jsou podle Wiresharku servisní informace USB.
Zajímala nás část 0040-0070.
Z jednoho vysílaného rámce nebylo nic jasné kromě symbolů MOCFT. Ukázalo se, že tyto symboly pocházejí ze souboru firmwaru, stejně jako zbytek symbolů až do konce rámce (soubor firmwaru je zvýrazněn):

Osobně jsem, stejně jako Ilja, neměl tušení, co znamenají symboly fd 3e 02 01 fe.
Podíval jsem se na následující snímek (servisní informace zde byly odstraněny, soubor s firmwarem je zvýrazněn):

Co se ukázalo? Že první dva bajty jsou nějaká konstanta. Všechny následující bloky to potvrdily, ale až na konci přenosového bloku:

Tento snímek mě také zmátl, protože konstanta (zvýrazněná) se změnila a kupodivu byla přítomna i část souboru. Velikost přenesených bajtů ukazovala, že bylo přeneseno 1024 bajtů. Co znamenají zbývající bajty – opět jsem neměl tušení.
První věc, kterou jsem jakožto zkušený BBSer udělal, bylo, že jsem si prošel standardní přenosové protokoly. Žádný z nich neuměl přenést 1024 bajtů. Začal jsem zkoumat hardware a narazil jsem na protokol 1K Xmodem. Umožňoval 1024 bajtů, ale s háčkem: zpočátku pouze 128 a pouze pokud nedošlo k žádným chybám, protokol zvýšil počet přenášených bajtů. Hned jsem přenášel 1024 bajtů. Rozhodl jsem se prostudovat přenosové protokoly, konkrétně Xmodem.
Existovaly dvě varianty modemu.
Nejprve formát paketu XMODEM s podporou CRC8 (původní XMODEM):

Za druhé, formát paketu XMODEM s podporou CRC16 (XmodemCRC):

Vypadá podobně, až na SOH, číslo pouzdra, CRC a délku pouzdra.
Podíval jsem se na začátek druhého přenosového bloku (a znovu jsem viděl soubor firmwaru, ale s odsazením 1024 bajtů):

Viděl jsem známou hlavičku, fd 3e 02, ale další dva bajty se již změnily: bylo 01 fe, nyní 02 fd. Pak jsem si všiml, že druhý blok je nyní očíslován 02, a tak jsem si uvědomil: toto je číslování bloku přenosu. První 1024 přenosů je 01, druhý je 02, třetí je 03 a tak dále (ale samozřejmě v hexadecimální soustavě). Ale co znamená změna z fe na fd? Mé oči viděly pokles o 1, můj mozek mi připomněl, že programátoři počítají od 0, ne od 1. Ale proč je pak první blok 1, ne 0? Na tuto otázku jsem nikdy nenašel odpověď. Ale pochopil jsem, jak se počítá druhý blok. Druhý blok není nic jiného než FF – (mínus) číslo prvního bloku. Druhý blok byl tedy označen jako = 02 (FF-02) = 02 FD. Následné čtení dumpu potvrdilo můj odhad.
Pak se začal objevovat následující obraz přenosu:
Začátek přenosu
fd 3e 02 – Start
01 FE – počítadlo přenosů
Přenos (34 bloků, přeneseno 1024 bajtů)
fd 3e 1024 bajtů dat (rozdělených do 30bajtových bloků).
Konec přenosu
fd 25
Data je třeba zarovnat na 1024 bajtů.
Jak vypadá rámec pro ukončení přenosu bloku:

fd 25 je signál pro ukončení přenosu bloku. Pak 2f 52 je zbytek souboru až do 1024 bajtů. 2f 52 je podle protokolu 16bitový kontrolní součet CRC.
Ze zvyku jsem v jazyce C vytvořil program, který ze souboru vytáhl 1024 bajtů a vypočítal 16bitový CRC. Spuštění programu ukázalo, že se nejedná o 16bitový CRC. Opět jsem byl na rozpacích – asi tři dny. Celou tu dobu jsem se snažil přijít na to, co by to mohlo být, když ne o kontrolní součet. Při hledání webových stránek v angličtině jsem zjistil, že X-modem používá vlastní výpočet kontrolního součtu – CRC-CCITT (XModem). Nenašel jsem žádné implementace tohoto výpočtu v jazyce C, ale našel jsem webovou stránku, která tento kontrolní součet online vypočítala. Po nahrání 1024 bajtů souboru na webovou stránku mi webová stránka zobrazila kontrolní součet, který se zcela shodoval s kontrolním součtem ze souboru.
Hurá! Poslední hádanka byla vyřešena, teď jsem si musel vytvořit vlastní firmware. Dále jsem své znalosti (které zůstaly jen v mé hlavě) předal Iljovi, který se vyzná v mocném nástroji – Pythonu.
Vytvoření programu
Příběh vypráví Ilja Alešin.
Poté, co jsem obdržel příslušné pokyny, jsem byl velmi „nadšený“.
Kde začít? Přesně tak, od začátku. Vyřazením USB portu.
Spuštění USB-pcap
Vyberte port, ke kterému je zařízení připojeno, a soubor, kam uložíme výpis.

Skener připojíme k počítači, na kterém je nainstalován nativní software EZConfigScanning pro Windows.

Tam najdeme možnost odesílat příkazy do zařízení. Ale co samotné příkazy? Kde je mohu získat?
Po spuštění programu se zařízení automaticky dotazuje (to uvidíme o něco později). Byly zde také tréninkové čárové kódy z oficiální dokumentace zařízení. VÝCHOZÍ. Toto je náš tým.

Potřebná data byla získána. Otevřete soubor dump.pcap pomocí Wiresharku.
Blokovat při spuštění EZConfigScanning. Oblasti vyžadující pozornost jsou zvýrazněny červeně.


Když jsem to všechno viděl poprvé, byl jsem sklíčený. Nebylo jasné, kam kopat dál.
Trocha brainstormingu a... Aha! Na smetišti out - je ina in tento out.
Vygooglil jsem si URB_INTERRUPT a zjistil jsem, že se jedná o metodu přenosu dat. Existují čtyři takové metody: řídicí, interrupční, izochronní a hromadná. Můžete si o nich přečíst samostatně.
A adresu koncového bodu v rozhraní USB zařízení lze získat buď pomocí příkazu „lsusb –v“, nebo pomocí pyusb.
Nyní musíme najít všechna zařízení s tímto VID. Můžete vyhledávat konkrétně podle VID:PID.
![]()
Vypadá to takto:


Takže máme potřebné informace: příkazy P_INFO nebo DEFALT, adresy, kam příkazy zapisovat (endpoint=03) a kam přijímat odpověď (endpoint=86). Zbývá už jen převést příkazy do hexadecimálního formátu.
![]()

Protože jsme zařízení již našli, odpojíme ho od jádra...

...a zapisovat do koncového bodu s adresou 0x03,

... a poté přečteme odpověď z koncového bodu s adresou 0x86.

Strukturovaná odpověď:
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: 0x6986Tato data vidíme v souboru dump.pcap.



Výborně! Převádíme čárové kódy systému do hexadecimálního formátu. To je vše, trénovací funkce je připravena.
A co firmware? Zdá se, že je stejný, ale je tu jeden detail.
Po zachycení kompletního dumpu procesu aktualizace firmwaru jsme získali hrubou představu o tom, s čím máme co do činění. Zde je článek o XMODEMU, který nám opravdu pomohl pochopit, jak tato komunikace funguje, i když jen v obecné rovině: Doporučuji si to přečíst.
Při pohledu na výpis vidíte, že velikost rámce je 1024 a velikost URB dat je 64.

Proto – 1024/64 – v bloku získáme 16 řádků, načteme soubor firmwaru znak po znaku a vytvoříme blok. V bloku přidáme jeden řádek se speciálními znaky fd3e02 + číslo bloku.
Dalších 14 řádků doplníme o fd25+, pomocí XMODEM.calc_crc() vypočítáme kontrolní součet celého bloku (trvalo dlouho, než jsme pochopili, že „FF – 1“ je CSUM) a poslední, 16. řádek doplníme o fd3e.
Zdálo by se, že to je vše: přečíst soubor s firmwarem, připojit se k blokům, odpojit skener od jádra a odeslat ho do zařízení. Ale není to tak jednoduché. Skener je třeba přepnout do režimu firmwaru.
отправив ему NEWAPP = ‘\xfd\x0a\x16\x4e\x2c\x4e\x45\x57\x41\x50\x50\x0d’.
Odkud tento příkaz pochází? Z výpisu.

Ale kvůli omezení 64 nemůžeme do skeneru poslat celý blok:
![]()
A skener v režimu blikání NEWAPP neakceptuje hexadecimální kód. Takže budete muset přeložit každý řádek pole 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]A poté tato data odešlete do skeneru.
Dostáváme odpověď:
[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]Pokud si přečtete článek o XMODEMU, bude jasné: data byla přijata.

Po přenosu všech bloků dokončíme přenos END_TRANSFER = 'xfdx01x04'.
Protože tyto bloky neposkytují běžným lidem žádné informace, budeme firmware ve výchozím nastavení spouštět v utajeném režimu. A pro jistotu nastavíme ukazatel průběhu pomocí tqdm.
![]()
Teď už jen zbývá zabalit řešení do skriptů pro hromadné nasazení v jasně definovaném čase, aby se nezpomaloval proces odbavení, a přidat logování.
Celkový
Po investování spousty času, úsilí a úsilí jsme byli schopni vyvinout potřebná řešení a dodrželi jsme termín. Navíc jsou skenery nyní centrálně přeprogramovány a přeškoleny, což nám dává přesnou kontrolu nad celým procesem. Společnost ušetřila čas i peníze a my jsme získali neocenitelné zkušenosti s reverzním inženýrstvím tohoto typu zařízení.
Zdroj: www.habr.com
