- पुराने Yamaha PSR-E433 synthesizer के SWL01U चिप को reverse engineering करके, सिर्फ USB-MIDI SysEx संदेशों से RAM में कोड लिखा गया और LCD पर Bad Apple वीडियो चलाया गया
- JTAG IDCODE
0x3f0f0f0fऔर OpenOCD/GDB प्रयोगों से पुष्टि हुई कि चिप ARM7TDMI core की तरह काम करता है, और अंदरूनी 64KiB ROM तथा बाहरी 16MiB flash firmware को dump किया गया - firmware के अंदर MIDI SysEx के ऊपर चलने वाला एक छिपा हुआ shell था, जिसमें
loginऔर पासवर्ड#0000के बाद memory read/write commands इस्तेमाल किए जा सकते थे - arbitrary memory write command से RAM में ARM कोड inject करने के बाद stack का return address overwrite किया गया, जिससे JTAG या UART के बिना सिर्फ MIDI file चलाकर code execution संभव हो गया
- LCD output को CGRAM control, task table copy, display task disable, और shell callback replacement के ज़रिए बेहतर किया गया, जिससे प्रति frame transfer 6732 bytes से घटकर 92 bytes रह गया
Yamaha PSR-E433 की आंतरिक जाँच
- लक्ष्य डिवाइस लंबे समय से इस्तेमाल किया जा रहा Yamaha PSR-E433 synthesizer था, और main board पर
DMLCDमार्किंग के साथ दो flash chips, एक RAM chip, औरYAMAHA SWL01Uchip मौजूद थे - SWL01U के बारे में सार्वजनिक जानकारी लगभग नहीं थी, और online मिली सिर्फ एक पोस्ट में कहा गया था कि यह SuperH CPU core पर आधारित हो सकता है
- समान मॉडल E443 की service manual में SWL01U pinout दिया गया था, जिसमें
TESTN,PROTN, दो bidirectional UART, और JTAG test points दिखाए गए थे - शुरुआती पहुंच के चार रास्ते थे
TESTN,PROTNpins को बदलकर boot mode में बदलाव देखना- UART Tx pin पर solder करके output जाँचना
- JTAG से chip identification code पढ़ना
- flash chip हटाकर firmware dump करना
TESTNको सक्रिय करने पर synthesizer boot नहीं हुआ, औरPROTNसे व्यवहार में कोई बदलाव नहीं आया- unused UART Tx pin पर सीधे solder करने के बाद भी
TESTN/PROTNके चारों combinations में कोई output नहीं मिला
JTAG से सामने आया ARM7TDMI व्यवहार
- JTAG में vendor-specific implementation के कारण circuit details समझने पड़ते हैं, लेकिन लगभग सभी devices में supported IDCODE read को OpenOCD से पहले आज़माया गया
- OpenOCD ने IDCODE
0x3f0f0f0fबताया, और यह value STMicroelectronics STR7xxx या Atmel SAM7xxx जैसे ARM7-आधारित microcontrollers से जुड़ी लग रही थी - SWL01U को OpenOCD के
arm7tdmitarget के रूप में सेट करने पर सही तरह से connection हो गया, और hardware में 2 breakpoint/watchpoint units होने की सूचना मिली - GDB से execution रोकने और फिर शुरू करने पर board current अनुमानित तरीके से बदला
- running के दौरान लगभग 115mA
- paused होने पर लगभग 98mA
- current में यह बदलाव इस बात का मजबूत संकेत था कि वास्तव में ARM7TDMI core को रोका और फिर चलाया जा रहा था
ROM और flash firmware dump
- ARM7TDMI documentation के अनुसार reset vector address
0पर होता है, और GDB से address0पढ़ने परldr pc, [pc, #24]जैसी jump instruction दिखाई दी - address
0से 16MiB dump करके Cutter में खोला गया, लेकिन strings हर 64KiB पर दोहराई जा रही थीं- उदाहरण:
SWL01U Internal0x0000bfd0,0x0001bfd0,0x0002bfd0आदि पर बार-बार मिला
- उदाहरण:
- repeat pattern और strings के आधार पर यह निष्कर्ष निकला कि यह dump external flash नहीं बल्कि chip की internal memory थी, और SWL01U में 64KiB ROM मौजूद है
- reset vector का jump target
0x02000000था, और इस address से फिर 16MiB dump करने पर कोई repetition नहीं थी - external flash dump में synthesizer इस्तेमाल करते समय दिखने वाली strings शामिल थीं
GrandPnoTr1 will be OverWritten!BogiWogi
- पुष्टि की गई memory layout इस प्रकार थी
- internal ROM:
0x00000000, 64KiB - external flash:
0x02000000, 16MiB - boot के समय ROM तुरंत control external flash को दे देता है
- internal ROM:
Ghidra से मिला छिपा हुआ shell
- Cutter से analysis पर्याप्त नहीं था, इसलिए Ghidra पर स्विच किया गया और strings व xref को follow करके firmware की संरचना समझी गई
help,?,info,verजैसी strings पास-पास मिलीं, और हर string ऐसे array से जुड़ी थी जो command name और function pointer की pair जैसा दिखता था- command handling function state machine शैली का था, और
loginstring तथाPasswd Errorstring से पुष्टि हुई कि यह login प्रक्रिया वाला shell है - shell input handling 256-byte circular buffer को iterate करके character-by-character processing करती थी, और
\rमिलने पर command execute होता था - login flow इस प्रकार था
loginडालने परpasswd?output- password
#0000डालने परlogin OK - उसके बाद commands चलाना संभव
- पहचाने गए shell commands में ये शामिल थे
logout,help,?,info,verstack,perf-on,perf-off,perf-dispd,dp,d xxxxx,d/s xxxxxm ADDRESS DATA,m/b ADDRESS DATA,m/w ADDRESS DATA,m/l ADDRESS DATA
infocommand ने ये जानकारी लौटाईDevelopName PSR-E433DevelopNumber #3341Main DevelopNumber #3341Make data & time MAY 16 2012 19:00:57J/E Select English
USB-MIDI SysEx पर चलने वाला shell
- shell output function हर byte को upper/lower 4-bit nibble में बाँटकर अलग bytes में रखता था, और आगे-पीछे fixed header व footer जोड़ता था
>prompt के लिए packet structure यह था- header:
F0 43 73 01 52 19 00 00 - payload:
03 0E 02 00 - footer:
F7
- header:
- MIDI SysEx message
0xF0से शुरू होकर manufacturer ID और payload से गुजरते हुए0xF7पर खत्म होता है, और payload में सिर्फ MSB 0 वाले bytes रखे जा सकते हैं - header का
0x43Yamaha manufacturer ID था, और shell packet structure Yamaha SysEx message format से मेल खाता था - synthesizer के USB descriptor में सिर्फ MIDI interface था, कोई अलग serial port नहीं था
- Python script से terminal और shell protocol के बीच conversion करने पर USB-MIDI के ज़रिए shell से बातचीत संभव हुई
MIDI Shellcode से code execution
- shell का
m/l AAAAAAAA DDDDDDDD\rcommand 32-bit memory write करता है, और address व data को hexadecimal ASCII के रूप में भेजता है - सिर्फ 4-byte payload लिखने पर भी वास्तविक transfer बहुत बढ़ जाता था
- command bytes को दो-दो 4-bit nibble bytes में बदला जाता था
- SysEx message में 9 bytes अतिरिक्त जुड़ते थे
- हर 3 bytes को 4-byte USB-MIDI packet में wrap किया जाता था
- 4-byte write के लिए synthesizer को 72 bytes भेजने पड़ते थे
- echo और prompt सहित कुल 396 bytes का आदान-प्रदान होता था
- RAM के एक apparently unused region को ढूँढकर वहाँ ARM assembly code रखा गया, और stack return address overwrite करके उसे चलाया गया
- पहला payload firmware के अंदर की string output function को call करता था और LCD के 8-character text area में
HeloWrldदिखाता था - यह तरीका JTAG या UART के बिना भी काम करता था, और messages को MIDI file में डालकर सिर्फ play करने से execute किया जा सकता था
- PSR-E433 firmware 1.02 के लिए MIDI file भी दी गई, लेकिन चेतावनी दी गई कि इसे दूसरे Yamaha devices या PSR-E433 के दूसरे firmware versions पर चलाने से व्यवहार अनिश्चित हो सकता है
LCD पर Bad Apple दिखाना
- Yamaha PSR-E433 का LCD controller ML9040A है, और मूल रूप से यह dot-matrix text characters संभालने के लिए बना था
- LCD में dot-matrix area के अलावा note symbols, 7-segment area, chord notation, और नीचे keyboard indicator area भी थे
- ML9040A में तीन तरह की memory थीं
- DDRAM: host इसमें display होने वाला character data लिखता है
- CGROM: character codes को graphic patterns में बदलता है
- CGRAM: host अधिकतम 8 custom characters define कर सकता है
- firmware, CGRAM को manipulate करके dot-matrix के नीचे वाले non-text display elements को control करता था, और इसी path से custom graphics दिखाना संभव था
- LCD controller को arbitrary data भेजने वाला firmware function खोजकर CGRAM में checker pattern डाला गया, लेकिन firmware लगातार CGRAM update करता रहा और उसे जल्दी overwrite कर देता था
RAM manipulation से display refresh control
- flash को सीधे overwrite करने पर device brick होने का जोखिम था, इसलिए सारे प्रयोग RAM manipulation तक सीमित रखे गए ताकि power restart से सब वापस हो सके
- firmware में primitive RTOS जैसी संरचना थी, और flash में 64 tasks की callback functions, stack, और attributes को define करने वाली global table थी
- boot के समय flash firmware ROM को task table location बताता था, और ROM उस location को internal SRAM की global variable में store कर देता था
- task table को RAM में copy करने के बाद अगर ROM को नई table इस्तेमाल करने के लिए बदल दिया जाए, तो flash बदले बिना task callbacks replace किए जा सकते थे
- display refresh task के callback को default idle callback से बदल दिया गया, ताकि firmware CGRAM को लगातार overwrite न कर सके
transfer efficiency और screen artifacts में सुधार
- Bad Apple का पहला implementation काम तो कर रहा था, लेकिन transfer efficiency कम होने से frame rate बहुत कम था और screen artifacts दिख रहे थे
- हर frame में सिर्फ CGRAM के 64 bytes और 32-bit return address overwrite भेजने पर भी वास्तविक transfer 70-byte payload पर 6732 bytes था
- कम efficiency के दो मुख्य कारण थे
- data को shell command format में wrap करना पड़ता था
- synthesizer हर character को बड़े SysEx packet के रूप में echo करता था
- shell task callback को खुद के बनाए callback से replace करके raw data लिया गया और response बंद किया गया, जिससे command wrapping और echo overhead हट गया
- अतिरिक्त packing optimization के बाद प्रति frame transfer 6732 bytes से 92 bytes तक घट गया, यानी 73 गुना कमी
- बचे हुए artifacts इसलिए थे क्योंकि LCD communication और panel button/LED scan एक ही 8 GPIO lines साझा करते थे
- अंतिम implementation में सीधे LCD पर लिखने के बजाय LCD/panel multiplexing task को panel scan पूरा होने के बाद इच्छित data भेजने के लिए कहा गया, जिससे distortion से बचा गया
अंतिम प्रक्रिया और आगे के analysis targets
- MIDI से LCD पर वीडियो दिखाने की अंतिम प्रक्रिया इस प्रकार थी
- shell में login करना
- shell के memory write command से RAM में executable code लिखना
- stack return address overwrite करके RAM code चलाना
- task table को RAM में copy करना
- नई task tables को इस तरह बदलना कि वे एक-दूसरे की ओर point करें
- ROM को नई task table इस्तेमाल करने के लिए बदलना
- display task callback को default idle callback से बदलना
- shell task callback को अपने callback से बदलना
- अपने callback में MIDI data unpack करके LCD/panel multiplexing task को देना
- MIDI के ज़रिए video frames भेजना
- SWL01U के MMIO region की समझ अभी सीमित है, और main ARM core से अलग DSP भी आगे analysis का विषय है
- संबंधित सामग्री
1 टिप्पणियां
Hacker News की राय
SuperH Sega 32X, Sega Saturn, Sega Dreamcast में भी लगा था, और HP Jornada जैसे कुछ शुरुआती Pocket PC में भी इस्तेमाल हुआ था
हालांकि ज़्यादातर Pocket PC ARM-आधारित थे
premise बेतुकी हद तक अजीब है, लेकिन यह सच में कर दिखाया गया—यह हैरान करने वाला है
“एक और packing optimization” का ज़िक्र था; मैं जानना चाहता हूं कि frames कैसे transmit किए जाते हैं
अगर dot matrix 7x5 के 8 characters है, तो कुल 280 bits, यानी प्रति frame 7-bit groups के 40 group हुए, लेकिन transmission में ऐसा लगता है कि उससे दोगुनी जगह लग रही है
क्या यह control data की वजह से waste हो रहा है, या transmission method थोड़ा sub-optimal है?
इसके साथ 9 bytes का packet header और footer जुड़ता है
लेख में शायद 92 लिखा है, लेकिन लगता है calculation गलत हो गई
पूरे 7 bits इस्तेमाल करने का तरीका ढूंढना बहुत मुश्किल था, इसलिए original तरीके की तुलना में optimal solution से बस थोड़ा-सा खराब solution चुना गया
अगर exact algorithm जानना हो, तो code अभी ठीक से व्यवस्थित नहीं है, लेकिन ये files देखी जा सकती हैं: https://github.com/portasynthinca3/swl01u/blob/master/fun/bi..., https://github.com/portasynthinca3/swl01u/blob/master/fun/ba...
अगर “reverse engineering का ज़्यादा experience नहीं है” का मतलब यह स्तर है, तो बाकी लोग किस level पर हैं, समझ नहीं आता
stage 1 में आप नए और उत्साही होते हैं लेकिन जानते हैं कि आप नहीं जानते; stage 2 “मैं भगवान हूं”; stage 3 “मैं बेवकूफ हूं” वाला phase है
“दुनिया का पहला MIDI shellcode” कहा गया, लेकिन major platforms में से ज़्यादातर पर MIDI shellcode 20 साल से भी पहले से मौजूद रहा है: https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=midi
जाहिर है यह SysEx ही था
standard MIDI में SysEx, Python में inline assembler जैसा है
लगभग हर MIDI device के अंदर undocumented proprietary चीजें छिपी होती हैं
MIDI keyboard लगाकर Am6,9/G# बजाया और root privileges वाला terminal window खुल जाए—वाकई कमाल लगेगा
हाल ही में मैंने देखा था कि MIDI fuzzing करने के लिए क्या चाहिए, और इसके लिए .mid files generate करने वाली सामग्री मिली, लेकिन वह मेरी चाहत से थोड़ी अलग थी
इसके बजाय यह दिशा explore करने लायक लगती है
अफसोस है कि आजकल synthesizers इसका इस्तेमाल कम करते जा रहे हैं, खासकर Roland
फिर भी Behringer अभी भी इसे काफी अच्छी तरह support करता है
उदाहरण के लिए Deepmind में MIDI CC range पहले से ही अच्छी है, और SysEx के जरिए लगभग 100% programmable है
कमाल की research है
इससे 2017 की वह research थोड़ी याद आती है जिसमें actual DNA/RNA molecules में shellcode synthesize करके DNA sequencing equipment पर remote code execution दिखाया गया था: https://www.usenix.org/conference/usenixsecurity17/technical...
मैं कहने वाला था “अगला OSC है क्या”, लेकिन लगता है अभी MIDI का ही दबदबा है
पूरा लेख पढ़ने की सलाह दूंगा, लेकिन मुझे लगता है मुख्य वाक्य ये हैं
“इन [keyboard manufacturer] पागलों ने USB के ऊपर MIDI SysEx messages पर चलने वाला shell बना रखा है”
“सबसे दिलचस्प command arbitrary memory read/write command है। चाहें तो MIDI के जरिए synthesizer memory में झांक सकते हैं और उसे poke कर सकते हैं”
“चाहें तो इन messages को MIDI file में लिखकर किसी दूसरी MIDI file की तरह synthesizer पर play कर सकते हैं। अरे, एक अच्छा idea आ रहा है…”
“firmware में घुसकर बिताई गई अनगिनत sleepless nights के बाद, मुझे arbitrary data को LCD controller तक भेजने वाला function मिला”
एक तरह से यह Internet of Things nightmare की हल्की झलक है
लगभग किसी भी device में backdoor हो सकता है, और वह #0000 जैसा बेवकूफाना backdoor भी हो सकता है
packet loss होना पूरी तरह normal है
सोच रहा हूं कि क्या Bad Apple commands के बीच MIDI music डालकर audio भी अपने-आप play कराया जा सकता है
repository README में लिखा है कि इसमें image dump है, लेकिन असल में नहीं है
सोच रहा हूं क्या यही intended state है
आखिरी समय में assembled code snippets को exclude करने के लिए
*.binको.gitignoreमें add किया था, लगता है dumps भी उसी के साथ exclude हो गएकुछ घंटों में upload कर दूंगा
वे dumps Yamaha copyright से protected हो सकते हैं, इसलिए यह उल्टा अच्छा फैसला भी हो सकता है
अगर HN readers में कोई Armenia में है, तो Porta 10 जनवरी को Hacker Embassy में इसी topic पर talk दे रहा है
ज़रूर आकर देखें: https://t.me/hackerembassy/17