3 पॉइंट द्वारा GN⁺ 2025-01-06 | 1 टिप्पणियां | WhatsApp पर शेयर करें
  • पुराने 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 SWL01U chip मौजूद थे
  • SWL01U के बारे में सार्वजनिक जानकारी लगभग नहीं थी, और online मिली सिर्फ एक पोस्ट में कहा गया था कि यह SuperH CPU core पर आधारित हो सकता है
  • समान मॉडल E443 की service manual में SWL01U pinout दिया गया था, जिसमें TESTN, PROTN, दो bidirectional UART, और JTAG test points दिखाए गए थे
  • शुरुआती पहुंच के चार रास्ते थे
    • TESTN, PROTN pins को बदलकर 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 के arm7tdmi target के रूप में सेट करने पर सही तरह से 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 से address 0 पढ़ने पर ldr pc, [pc, #24] जैसी jump instruction दिखाई दी
  • address 0 से 16MiB dump करके Cutter में खोला गया, लेकिन strings हर 64KiB पर दोहराई जा रही थीं
    • उदाहरण: SWL01U Internal 0x0000bfd0, 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 शामिल थीं
    • GrandPno
    • Tr1 will be OverWritten!
    • BogiWogi
  • पुष्टि की गई memory layout इस प्रकार थी
    • internal ROM: 0x00000000, 64KiB
    • external flash: 0x02000000, 16MiB
    • boot के समय ROM तुरंत control external flash को दे देता है

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 शैली का था, और login string तथा Passwd Error string से पुष्टि हुई कि यह 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, ver
    • stack, perf-on, perf-off, perf-disp
    • d, dp, d xxxxx, d/s xxxxx
    • m ADDRESS DATA, m/b ADDRESS DATA, m/w ADDRESS DATA, m/l ADDRESS DATA
  • info command ने ये जानकारी लौटाई
    • DevelopName PSR-E433
    • DevelopNumber #3341
    • Main DevelopNumber #3341
    • Make data & time MAY 16 2012 19:00:57
    • J/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
  • MIDI SysEx message 0xF0 से शुरू होकर manufacturer ID और payload से गुजरते हुए 0xF7 पर खत्म होता है, और payload में सिर्फ MSB 0 वाले bytes रखे जा सकते हैं
  • header का 0x43 Yamaha 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\r command 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 टिप्पणियां

 
GN⁺ 2025-01-06
Hacker News की राय
  • SuperH Sega 32X, Sega Saturn, Sega Dreamcast में भी लगा था, और HP Jornada जैसे कुछ शुरुआती Pocket PC में भी इस्तेमाल हुआ था
    हालांकि ज़्यादातर Pocket PC ARM-आधारित थे

    • इसका industrial use में भी काफी इस्तेमाल हुआ, और Mitsubishi ने Lancer Evolution सहित कुछ गाड़ियों के ECU में इस chip का इस्तेमाल किया था
  • 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 है?

    • dot matrix असल में 5x8 characters के 8 हैं, इसलिए कुल 320 bits होते हैं, और इन 320 bits को shell protocol में प्रति byte उपलब्ध 4 bits में pack किया जा रहा है
      इसके साथ 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 पर हैं, समझ नहीं आता

    • बहुत कुछ जानते हुए भी यह समझना कि आप कुछ नहीं जानते, experience के चौथे stage जैसा लगता है
      stage 1 में आप नए और उत्साही होते हैं लेकिन जानते हैं कि आप नहीं जानते; stage 2 “मैं भगवान हूं”; stage 3 “मैं बेवकूफ हूं” वाला phase है
    • खासकर बहुत प्रतिभाशाली non-professional engineers अक्सर ऐसी बातें कहते हैं
  • “दुनिया का पहला MIDI shellcode” कहा गया, लेकिन major platforms में से ज़्यादातर पर MIDI shellcode 20 साल से भी पहले से मौजूद रहा है: https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=midi

    • buffer overflow तो बहुत हैं, लेकिन उन vulnerabilities के लिए सच में किसी ने shellcode लिखा था या नहीं, यह अलग बात है
  • जाहिर है यह SysEx ही था
    standard MIDI में SysEx, Python में inline assembler जैसा है
    लगभग हर MIDI device के अंदर undocumented proprietary चीजें छिपी होती हैं

    • अच्छा होगा अगर कोई गाना बजाकर remote code execution trigger किया जा सके
      MIDI keyboard लगाकर Am6,9/G# बजाया और root privileges वाला terminal window खुल जाए—वाकई कमाल लगेगा
    • Google पिछले कुछ सालों से Chrome MIDI support बना रहा है; इसमें वे कौन-सा SysEx hacking ठूंसेंगे, यह देखने का इंतज़ार है
    • मुझे पता ही नहीं था कि ऐसी दुनिया भी है
      हाल ही में मैंने देखा था कि MIDI fuzzing करने के लिए क्या चाहिए, और इसके लिए .mid files generate करने वाली सामग्री मिली, लेकिन वह मेरी चाहत से थोड़ी अलग थी
      इसके बजाय यह दिशा explore करने लायक लगती है
    • SysEx शानदार है
      अफसोस है कि आजकल 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 मिला”

    • अब असली सवाल यह है कि क्या keyboard पर चल रहे code को बदलकर ऐसा बनाया जा सकता है कि उसी model का कोई दूसरा keyboard जब यह MIDI data receive करे, तो infect करने की कोशिश करे
      एक तरह से यह Internet of Things nightmare की हल्की झलक है
      लगभग किसी भी device में backdoor हो सकता है, और वह #0000 जैसा बेवकूफाना backdoor भी हो सकता है
    • इसे MIDI file की तरह play करने पर output शायद dubstep होगा
    • MIDI से synthesizer memory पढ़ना और लिखना सुनने में आसान लगता है, लेकिन SysEx में delivery guarantee नहीं होती, और connection या session का concept भी नहीं होता, इसलिए यह परेशान करने वाला हो सकता है
      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