
Koupil jsem na AliExpressu blikací hračku LED Fluid Simulation – takovou tu desku plnou LED, co simuluje přelévání tekutiny podle náklonu. Po rozebrání mě ale začalo zajímat něco jiného než to, co dělá. Jde to hacknout a vyčíst program a poté ho dekódovat?
Spoiler hned na začátku: firmware jsem nedostal. Tak se o to podělím – ať vám to ušetří pár hodin, až narazíte na to samé. Teda spíš na obdobný hardware.
Uvnitř hračky je PY32F403C1 (datasheet) – levný 32bitový ARM Cortex-M4 od čínské Puyi. Tyhle čipy stojí pakatel.
Reverse engineering pro vlastní studium.
Co jsem měl po ruce
- Nucleo se zabudovaným ST-Linkem (konkrétně NUCLEO-C031C6)
- programátor WCH-LinkE
- multimetr, drátky, trochu trpělivosti
- Windows
Plán byl jednoduchý: připojit se přes SWD, vyčíst flash, hotovo. A třeba zkusit dekódovat program.
Realita byla o něco pestřejší.
Hledání SWD
Na desce jsem proměřil a našel tři kontakty – SWDIO, SWCLK a reset. V datasheetu jsem si našel, kam jsou připojené a pak jen propípal testpady na desce.
To je základ. Přes SWD (Serial Wire Debug) se k ARM jádru dostanete dvěma vodiči plus zem.
První pokus byl s WCH-LinkE. A tady přišel první kámen úrazu.
WCH-LinkE a věčný boj s ovladači
WCH-LinkE je šikovný a levný programátor, umí ARM i RISC-V. Jenže na Windows ho rozchodit s PyOCD nebo OpenOCD chce mít správný ovladač – a ten Windows samy od sebe nenasadí. Zkoušel jsem Zadig, instaloval WinUSB ovladač, a stejně ho systém pořád neviděl.
Na Linuxu jsem přitom problém neměl (jak jsem programoval mikra CH32). Asi problém mezi židlí a klávesnicí.
Po chvíli jsem to vzdal. Když máte po ruce ST-Link na Nucleu, který má v PyOCD i OpenOCD mnohem lepší podporu.
Na příkaz
pyocd list --probes
Vždy odpověděl
No available debug probes
ST-Link z Nucleo
Integrovaný ST-Link na Nucleu jde použít i na externí čip. Stačí sundat oba jumpery CN2 (tím odpojíte ST-Link od STM32 na samotném Nucleu) a vyvést SWD signály na externí desku.
Jenže STM32CubeProgrammer hlásil „Cannot identify the device“. A tady začala detektivka.
Detektivka jménem „proč to nekomunikuje“
Prošel jsem klasicky:
- Napájení? Deska měla vlastní LiPol a měnič na 3,3 V, multimetrem jsem ověřil, že na čipu reálně 3,3 V je.
- Společná zem? Propojená.
- Jumpery CN2? Sundané.
- Zapojení SWDIO/SWCLK? Proměřené, sedělo.
Tady stojí za to zmínit chování CubeProgrammeru, které mě chvíli mátlo. Se správně zapojenými datovými (SWDIO) a hodinovými vodiči (SWCLK) hlásil „Cannot identify the device“. Když jsem SWDIO/SWCLK odpojil, hláška se změnila na „No STM32 target found“.
Působilo to nelogicky (byla skoro půlnoc), ale dává to smysl (teď ráno). Bez datového vodiče ST-Link nedostane žádnou odpověď, a tak hlásí, že nenašel cíl. Se správným zapojením naopak komunikace běží – jenže CubeProgrammer hledá konkrétně STMkový čip a PY32 podle ID nepozná.
A to je celé jádro problému: CubeProgrammer je dělaný na STMka, PY32 mu nic neřekne. I když je PY32F403 klon STM32 🫣.
CubeProgrammer tedy nedopadl. Přešel jsem na OpenOCD, který umí pracovat i s generickým Cortex-M targetem bez kontroly, že je to zrovna STMko.

OpenOCD a záhada nulového napětí
Tady jsem narazil na věc, která mě taky vypálila.
OpenOCD hlásil:
Info : Target voltage: 0.000000
Error: target voltage may be too low for reliable debugging

Nula voltů? Vždyť na čipu mám změřené 3,3 V!
Jenže ST-Link to napětí měří na svém pinu VTREF (target reference voltage) – na tom, který má být propojený s napájením cílové desky. ST-Link potřebuje vědět, na jakou napěťovou úroveň má nastavit SWD signály. A když na VTREF nevidí napětí, odmítne komunikovat. I kdyby čip sám byl napájený jak chce.
Řešení: propojit VTREF s 3,3 V. Protože jsem nechtěl míchat dva zdroje napájení, vyřešil jsem to napájením desky přímo z Nucleo (s odpojenou baterií). Najednou:
Info : Target voltage: 3.231373

A je to tady – jádro chyceno
Po doladění konečně:
Info : SWD DPIDR 0x2ba01477
Info : [py32.cpu] Cortex-M4 r0p1 processor detected
Info : [py32.cpu] target has 6 breakpoints, 4 watchpoints
Info : [py32.cpu] Examination succeed
xPSR: 0x61000000 pc: 0x080091d8 msp: 0x20009818
Nádhera. Jádro detekováno, zastaveno, program counter ukazuje do flash, stack pointer do RAM. Všechno sedí. Čip žije, komunikuje, jsem dovnitř.
Teď už jen vyčíst flash:
dump_image firmware.bin 0x08000000 0xC000
A pak to přišlo…
RDP ochrana: zavřená cesta
Error: Failed to read memory at 0x08000004

Jakýkoli pokus o čtení paměti skončil chybou. Jádro se detekuje, dá se zastavit – ale flash nevydá ani bajt.
To je učebnicový příznak aktivní RDP (Read-Out Protection) – čtecí ochrany. Vyzkoušel jsem připojení pod resetem (connect_assert_srst), reset halt, snížení rychlosti až na 15 kHz, čtení po jednotlivých slovech. Jádro se i čistě zastavilo na reset vektoru. A flash pořád mlčela.
Takže je to jasný je: firmware je za čtecí ochranou a ven ho nedostanu.
Čínská hračka za pár korun má zamčenou paměť…
Proč to nejde obejít
RDP je hardwarová bariéra – ochrana. Je přesně od toho, aby zabránila vyčtení flash přes debug rozhraní – to, co jsem chtěl udělat já. A smazání není řešení – zablokoval by se tím (nejspíš) JTAG, takže by se k němu nešlo poté připojit.
Tady je otázka, jaký level RDP PY32F403 používá respektive byl aktivován.
Existují i pokročilejší metody – dekapsulace čipu, glitching, voltage fault injection. Ale to je obří kapitola sama o sobě, vyžaduje speciální vybavení, čas a expertízu. Pro blikající hračku za pár korun to absolutně nedává smysl.
Doplnění: šlo by RDP přece jen obejít?
Po publikaci mi dorazil tip od MarSika (https://witter.cz/@marsik) na zajímavou práci, která ukazuje, že RDP není úplně neprůstřelná zeď.
Dvojice výzkumníků popsala, jak na čipu STM32F4 obešli ochranu RDP1 pomocí takzvaného voltage glitchingu – tedy fault injection útoku.
Princip je prostý na papíře, brutální v praxi: do napájení čipu se v přesně načasovaný okamžik uměle vytvoří krátký napěťový pokles, který procesor donutí přeskočit zrovna tu instrukci, co kontroluje, jestli je čtecí ochrana aktivní. Když se to trefí, bootloader vydá data, která by jinak zamkl.
K tomu použili desku Teensy jako glitcher, vlastní injektážní obvod s MOSFETy a kondenzátory napájený přes pin VCAP, osciloskop na ladění a hlavně hromadu trpělivosti – celý firmware vytáhli po zhruba 22 hodinách a 1024 úspěšných glitchích z víc než 600 tisíc pokusů.
Je to velmi zajímavé čtení, ale zároveň ukázka, že tahle cesta je úplně jiná liga než připojit ST-Link: nevratný zásah do hardwaru, speciální vybavení a dny ladění.
A pozor – tahle práce cílí konkrétně na STM32F4. PY32F403 je sice klon, který se chová podobně, ale není zaručeně identický. Power management, umístění VCAP pinů i chování bootloaderu se můžou lišit, takže celou charakterizaci by bylo nutné dělat pro PY32 od nuly a není jisté, že by stejný postup vůbec zabral.
Pro firmware blikací hračky to rozhodně nestojí za to, ale jako důkaz, že RDP má své meze, je to skvělé. Zdroj: https://jerinsunny.github.io/stm32_vglitch/
Celý postup řídil AI agent
A teď to nejzajímavější, k čemu se musím přiznat. Celé tohle ladění jsem neřešil sám. Strategii vedl AI agent (Claude Opus 4.8 High) – já byl k ruce u hardwaru a oči na obrazovce. Cvičená opička? 😀
Fungovalo to jako dialog. Popsal jsem, co mám a co chci. Agent navrhl postup, já zapojil drátky a spustil příkaz, zkopíroval výstup zpět. Agent ho přečetl, určil další krok. A tak pořád dokola. Jo, cvičená opička. 🫣
Konkrétně mi agent:
- navrhl celou strategii – od prvního pokusu přes WCH-LinkE až po přechod na OpenOCD,
- diagnostikoval chybové hlášky a říkal, co znamenají,
- psal konkrétní příkazy pro OpenOCD a obsah konfiguračního souboru
py32.cfgpro generický Cortex-M target, - správně určil, že mlčící flash při funkčním připojení k jádru je příznak RDP – a ne chyba zapojení.
Není to o tom, že by AI „hackovala za mě“. Ale držela strukturu, pamatovala si kontext napříč desítkami pokusů a hlavně – každá chybová hláška pro ni byla informace, ne slepá ulička.
A tady je pointa, kterou si z toho odnáším: s dobrým AI agentem se do reverse engineeringu hardwaru může pustit i člověk, který není expert na SWD ani na OpenOCD (já 😀). Agent zná syntaxi příkazů, zná typické chyby, zná paměťovou mapu Cortex-M. Vy dodáte ruce, trpělivost a ochotu zkoušet. Jak z AI dostat takhle použitelné odpovědi – správně zadat hardware, cíl i omezení – rozebírám podrobně v článku Jak programovat Arduino, ESP32 a Raspberry Pi s pomocí AI.
Co jsem si odnesl
Firmware jsem nedostal. Ale to je vlastně dobrá zpráva o jiné věci: i ten nejlevnější čip z nejlevnější hračky může mít zapnutou čtecí ochranu. Počkat , vždyť to není dobrá zpráva! 😩
Puya RDP funguje. Spousta lidí automaticky předpokládá, že čínský no-name hardware je otevřený dokořán – a tady vidíte, že ne vždy. Bohužel.
A hlavně ta cesta. Většina problémů, na které jsem narazil, nemá nic společného s tím konkrétním čipem, ale s obecným laděním SWD:
- Nelpěte na jednom programátoru. Když WCH-LinkE nejde rozchodit, ST-Link to možná zvládne (a naopak).
- VTREF musí vidět napětí. Čip může být napájený, a stejně to nepojede, dokud ST-Link nevidí referenční napětí. To jsem nevěděl 😐 nebo AI agent kecal.
- Pozor na chování CubeProgrammeru. „Cannot identify the device“ u PY32 neznamená nutně špatné zapojení – CubeProgrammer prostě nepozná ne-STM čip podle ID. Nenechte se tím poslat na špatnou stopu.
- OpenOCD je univerzálnější. Pro cizí Cortex-M je s generickým targetem mnohem flexibilnější než CubeProgrammer.
Takže ano, neúspěch. Ale takový ten poučný. Z pohledu levných mikrokontrolérů a využití AI agenta.
Poznámka k produktu: Zapíná se jediným tlačítkem na desce z boku. Krabička je 3D tištěná.
Vypíná se tak, že jej položíte LEDkama dolů a cca 10 sekund se toho nebudete dotýkat – vypne se to samo.
Odkaz na LED Fluid Simulation

Použité nástroje: OpenOCD (xPack build), ST-Link na NUCLEO-C031C6, Zadig (WinUSB ovladač), multimetr a hodně trpělivosti.
PY32, které lze jednoduše programovat
Pokud vás PY32 zaujal, ale chcete hladší vstup než boj s F403, sáhněte po levných čipech řady PY32F002, PY32F003 nebo PY32F030 – to jsou ty nejrozšířenější kousky za pár korun, na kterých stojí celá komunitní podpora.
Na rozdíl od F403 (Cortex-M4) jde o jednodušší jádra Cortex-M0+, ale právě pro ně existuje hotová „stáhni a piš“ infrastruktura: komunitní Arduino core (py32duino/Arduino-PY32) umožňuje programovat je rovnou z Arduino IDE ve známém stylu, PlatformIO má pro F003/F030 připravené board definice a hotové GCC/CMake šablony (IOsetting/py32f0-template, decaday/py32f0-cmake) zvládnou build během pár minut.
Nahrávat je můžete tím samým ST-Linkem, co už máte rozchozený, nebo levným DAPLinkem. Pro učení a první blikání LED je tahle řada mnohem příjemnější vstup – F403 si necháte na chvíli, až budete mít chuť bastlit toolchain ručně.








Komentáře na sítích