3 puan yazan GN⁺ 2025-01-06 | 1 yorum | WhatsApp'ta paylaş
  • Eski bir Yamaha PSR-E433 synthesizer’daki SWL01U çipi tersine mühendislik ile incelenerek, yalnızca USB-MIDI SysEx mesajlarıyla RAM’e kod yazıldı ve LCD’de Bad Apple videosu gösterildi
  • JTAG IDCODE 0x3f0f0f0f ile OpenOCD/GDB deneyleri, çipin ARM7TDMI çekirdeği gibi davrandığını doğruladı; dahili 64KiB ROM ve harici 16MiB flash firmware dump edildi
  • Firmware içinde MIDI SysEx üzerinde çalışan gizli bir shell vardı; login ve #0000 parolasından sonra bellek okuma/yazma komutları kullanılabiliyordu
  • Keyfi bellek yazma komutuyla RAM’e ARM kodu enjekte edilip stack’teki dönüş adresi ezilerek, JTAG veya UART olmadan yalnızca MIDI dosyası çalarak kod çalıştırmak mümkün oldu
  • LCD çıktısı; CGRAM kontrolü, görev tablosu kopyalama, görüntüleme görevinin devre dışı bırakılması ve shell callback’inin değiştirilmesiyle iyileştirildi; kare başına aktarım miktarı 6732 bayttan 92 bayta düşürüldü

Yamaha PSR-E433’ün İçinin İncelenmesi

  • Hedef cihaz, uzun süredir kullanılan bir Yamaha PSR-E433 synthesizer’dı; ana kartta DMLCD ibaresiyle birlikte iki flash çipi, bir RAM çipi ve YAMAHA SWL01U çipi bulunuyordu
  • SWL01U hakkında herkese açık bilgi neredeyse yoktu; çevrimiçi bulunan tek bir yazı, bunun SuperH CPU çekirdeği tabanlı olabileceğinden bahsediyordu
  • Benzer model E443’ün servis kılavuzunda SWL01U pin dizilimi vardı; TESTN, PROTN, iki çift yönlü UART ve JTAG test noktaları gösterilmişti
  • İlk yaklaşım dört koldan ilerledi
    • TESTN, PROTN pinlerini değiştirerek boot modu değişikliklerini kontrol etmek
    • UART Tx pinine lehim yaparak çıktıyı kontrol etmek
    • JTAG ile çip tanımlama kodunu okumak
    • Flash çipini sökerek firmware dump almak
  • TESTN etkinleştirildiğinde synthesizer boot etmiyordu; PROTN ise davranışta bir değişiklik yaratmadı
  • Kullanılmıyor gibi görünen UART Tx pinine doğrudan lehim yapıldı, ancak TESTN/PROTN için dört kombinasyonun hiçbirinde çıktı alınmadı

JTAG ile Ortaya Çıkan ARM7TDMI Davranışı

  • JTAG, üreticiye özgü uygulamalara göre ayrıntılı devre açıklaması gerektirse de, neredeyse tüm cihazların desteklediği IDCODE okuması önce OpenOCD ile denendi
  • OpenOCD, IDCODE’u 0x3f0f0f0f olarak raporladı; bu değer STMicroelectronics STR7xxx veya Atmel SAM7xxx serisi gibi ARM7 tabanlı mikrodenetleyicilerle ilişkilendirilebilir görünüyordu
  • SWL01U, OpenOCD’de arm7tdmi hedefi olarak belirtildiğinde düzgün biçimde bağlandı ve donanımda 2 breakpoint/watchpoint birimi olduğu gösterildi
  • GDB ile yürütme durdurulup yeniden başlatıldığında kart akımı öngörülebilir şekilde değişti
    • Çalışırken yaklaşık 115mA
    • Duraklatılmışken yaklaşık 98mA
  • Bu akım değişimi, gerçekten ARM7TDMI çekirdeğinin durdurulup yeniden başlatıldığına dair güçlü bir işaret oldu

ROM ve Flash Firmware Dump’ı

  • ARM7TDMI belgelerine göre reset vector adres 0’dadır; GDB ile adres 0 okunduğunda ldr pc, [pc, #24] biçiminde bir atlama komutu görüldü
  • Adres 0’dan 16MiB dump alınıp Cutter ile açıldı, ancak dizgeler her 64KiB’de bir tekrarlanıyordu
    • Örn: SWL01U Internal, 0x0000bfd0, 0x0001bfd0, 0x0002bfd0 vb. adreslerde tekrarlanıyordu
  • Tekrarlayan desen ve dizgeler nedeniyle bu dump’ın harici flash değil, çipin dahili belleği olduğu değerlendirildi; SWL01U’da 64KiB ROM bulunduğu sonucuna varıldı
  • Reset vector’ün atlama hedefi 0x02000000 idi; bu adresten tekrar 16MiB dump alındığında tekrar yoktu
  • Harici flash dump’ında synthesizer kullanımı sırasında görülebilen dizgeler vardı
    • GrandPno
    • Tr1 will be OverWritten!
    • BogiWogi
  • Doğrulanan bellek düzeni şöyleydi
    • Dahili ROM: 0x00000000, 64KiB
    • Harici flash: 0x02000000, 16MiB
    • Boot sırasında ROM, kontrolü hemen harici flash’a devrediyor

Ghidra ile Bulunan Gizli Shell

  • Cutter tek başına analiz için yetersiz kaldığından Ghidra’ya geçildi; dizgeler ve xref’ler takip edilerek firmware yapısı anlaşıldı
  • help, ?, info, ver gibi dizgeler yakın adreslerde kümelenmişti; her dizge, komut adı ve fonksiyon işaretçisi çiftlerinden oluşuyor gibi görünen bir diziye bağlıydı
  • Komut işleme fonksiyonu bir durum makinesi biçimindeydi; login dizgesi ve Passwd Error dizgesi üzerinden bunun giriş prosedürü olan bir shell olduğu doğrulandı
  • Shell giriş işleme, 256 baytlık halka buffer üzerinde dolaşıp karakter karakter çalışıyor; \r karakteriyle karşılaşınca komutu çalıştıracak şekilde tasarlanmıştı
  • Giriş akışı şöyleydi
    • login girilince passwd? çıktısı
    • Parola olarak #0000 girilince login OK
    • Sonrasında komut çalıştırma mümkün
  • Doğrulanan shell komutları arasında şunlar vardı
    • 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 komutu şu bilgileri döndürdü
    • DevelopName PSR-E433
    • DevelopNumber #3341
    • Main DevelopNumber #3341
    • Make data & time MAY 16 2012 19:00:57
    • J/E Select English

USB-MIDI SysEx Üzerinde Çalışan Shell

  • Shell çıktı fonksiyonu, her baytı üst/alt 4 bitlik nibble’lara ayırıp ayrı baytlara koyuyor ve başına/sonuna sabit header ve footer ekliyordu
  • > prompt’una karşılık gelen paket yapısı şöyleydi
    • Header: F0 43 73 01 52 19 00 00
    • Payload: 03 0E 02 00
    • Footer: F7
  • MIDI SysEx mesajı 0xF0 ile başlar, üretici ID’si ve payload’dan geçerek 0xF7 ile biter; payload’da yalnızca MSB’si 0 olan baytlar bulunabilir
  • Header’daki 0x43, Yamaha üretici ID’siydi ve shell paket yapısı Yamaha SysEx mesaj biçimiyle uyuşuyordu
  • Synthesizer’ın USB descriptor’ında yalnızca MIDI arayüzü vardı; ayrı bir seri port yoktu
  • Bir Python betiğiyle terminal ile shell protokolü arasında dönüşüm yapılınca USB-MIDI üzerinden shell ile iletişim kurulabildi

MIDI Shellcode ile Kod Çalıştırma

  • Shell’in m/l AAAAAAAA DDDDDDDD\r komutu 32 bit bellek yazma işlemi yapıyor; adres ve veri 16’lık tabanda ASCII olarak aktarılıyordu
  • 4 baytlık payload yazma sürecinde bile gerçek aktarım miktarı ciddi biçimde şişiyordu
    • Komut baytlarının her biri iki 4 bitlik nibble baytına dönüştürülüyor
    • SysEx mesajına 9 bayt ekleniyor
    • Her 3 bayt, 4 baytlık USB-MIDI paketiyle sarılıyor
    • 4 bayt yazmak için synthesizer’a 72 bayt göndermek gerekiyor
    • Echo ve prompt dahil edildiğinde toplam 396 bayt gidip geliyor
  • Kullanılmıyor gibi görünen bir RAM bölgesi bulunarak ARM assembly kodu yerleştirildi ve stack dönüş adresi ezilerek bu kod çalıştırıldı
  • İlk payload, firmware içindeki dizge çıktı fonksiyonunu çağırıp LCD’nin 8 karakterlik metin alanında HeloWrld gösterdi
  • Bu yöntem JTAG veya UART olmadan da çalışıyor; mesajı bir MIDI dosyasına koyup çalmak yeterli oluyordu
  • PSR-E433 firmware 1.02 için bir MIDI dosyası da sağlandı; ancak başka Yamaha cihazlarda veya farklı firmware sürümüne sahip PSR-E433’te çalınırsa öngörülemez davranabileceği konusunda uyarıldı

Bad Apple’ı LCD’de Göstermek

  • Yamaha PSR-E433’ün LCD denetleyicisi ML9040A; temel olarak nokta matrisli metin karakterlerini işleyen bir yapıya sahipti
  • LCD’de nokta matris alanının yanı sıra nota gösterimi, 7 segment alanı, akor gösterimi ve altta klavye gösterim alanı da vardı
  • ML9040A’da üç tür bellek vardı
    • DDRAM: Gösterilecek karakter verisini host yazar
    • CGROM: Karakter kodlarını grafik desenlere dönüştürür
    • CGRAM: Host en fazla 8 kullanıcı tanımlı karakter tanımlar
  • Firmware, CGRAM’i değiştirerek nokta matrisin altındaki metin dışı görüntü öğelerini kontrol ediyordu; bu yol kullanılarak kullanıcı tanımlı grafik gösterilebiliyordu
  • LCD denetleyicisine keyfi veri gönderen firmware fonksiyonu bulunup CGRAM’e dama tahtası deseni yüklendi, ancak firmware CGRAM’i sürekli güncellediği için kısa süre sonra üzerine yazdı

RAM Manipülasyonuyla Ekran Yenilemeyi Kontrol Etme

  • Flash’ı doğrudan ezmek cihazı kullanılamaz hâle getirme riski taşıdığından, tüm deneyler güç yeniden başlatmasıyla geri döndürülebilecek şekilde RAM manipülasyonu ile sınırlandırıldı
  • Firmware’de ilkel bir RTOS gibi görünen bir yapı vardı; flash’ta 64 görevin callback fonksiyonlarını, stack’lerini ve özniteliklerini tanımlayan global bir tablo bulunuyordu
  • Boot sırasında flash firmware, ROM’a görev tablosu konumunu bildiriyor; ROM da bu konumu dahili SRAM’deki global değişkende saklıyordu
  • Görev tablosu RAM’e kopyalanıp ROM’un yeni tabloyu kullanması sağlanınca, flash değiştirilmeden görev callback’leri değiştirilebiliyordu
  • Görüntü yenileme görevinin callback’i varsayılan idle callback ile değiştirilerek firmware’in CGRAM’in üzerine sürekli yazması engellendi

Aktarım Verimliliği ve Ekran Bozulmalarının İyileştirilmesi

  • Bad Apple’ın ilk uygulaması çalıştı, ancak aktarım verimliliği düşük olduğundan kare hızı çok düşüktü ve ekranda artefaktlar vardı
  • Her karede yalnızca 64 bayt CGRAM ve 32 bit dönüş adresi ezme gönderilse bile gerçek aktarım miktarı 70 bayt payload başına 6732 bayt idi
  • Düşük verimliliğin iki ana nedeni vardı
    • Verinin shell komutu biçiminde sarılması gerekiyordu
    • Synthesizer her komutu karakter karakter büyük SysEx paketleriyle echo ediyordu
  • Shell görev callback’i, doğrudan yazılmış bir callback ile değiştirilip raw veri alacak ve yanıt vermeyecek hâle getirilince, komut sarmalama ve echo ek yükü kaldırılabildi
  • Ek paketleme optimizasyonlarından sonra kare başına aktarım miktarı 6732 bayttan 92 bayta düştü; bu 73 kat azalma demekti
  • Kalan artefaktlar, LCD iletişimi ile panel düğmesi/LED taramasının aynı 8 GPIO hattını paylaşmasından kaynaklanıyordu
  • Son uygulama LCD’ye doğrudan yazmak yerine, LCD/panel çoklama görevinin panel taramasını bitirdikten sonra istenen veriyi göndermesini talep ederek bozulmayı önledi

Nihai Çalışma Prosedürü ve Kalan Analiz Konuları

  • MIDI ile LCD görüntüsü göstermenin nihai prosedürü şöyle
    • Shell’e giriş yap
    • Shell’in bellek yazma komutuyla yürütülecek kodu RAM’e yaz
    • Stack dönüş adresini ezerek RAM kodunu çalıştır
    • Görev tablosunu RAM’e kopyala
    • Yeni görev tablolarını birbirlerini gösterecek şekilde değiştir
    • ROM’un yeni görev tablosunu kullanmasını sağla
    • Görüntüleme görevi callback’ini varsayılan idle callback ile değiştir
    • Shell görev callback’ini kendi callback’inle değiştir
    • Kendi callback’inde MIDI verisini unpack edip LCD/panel çoklama görevine ilet
    • MIDI ile video karelerini besle
  • SWL01U’nun MMIO bölgesi hakkındaki anlayış hâlâ sınırlı; ana ARM çekirdeğinden ayrı olan DSP de analiz edilmeyi bekleyen konular arasında
  • İlgili materyaller

1 yorum

 
GN⁺ 2025-01-06
Hacker News yorumları
  • SuperH, Sega 32X, Sega Saturn ve Sega Dreamcast’te de yer aldı; HP Jornada gibi bazı erken dönem Pocket PC’lerde de kullanıldı
    Ancak Pocket PC’lerin çoğu ARM tabanlıydı

    • Endüstriyel alanda da yaygın kullanıldı; Mitsubishi bu çipi Lancer Evolution dahil bazı araçlarının ECU’larında kullandı
  • Ön kabul neredeyse saçma denecek kadar uçuk, ama bunu gerçekten başarmış olmaları şaşırtıcı
    “Başka bir paketleme optimizasyonu”ndan söz etmişler; kareleri nasıl ilettiklerini merak ediyorum
    Nokta matrisi 7x5 karakterden 8 adet ise toplam 280 bit, yani kare başına 7 bitlik 40 grup eder; ama iletimde bunun iki katı kadar alan kullanıyor gibi görünüyor
    Bunun kontrol verisi yüzünden mi boşa gittiğini, yoksa iletim yönteminin biraz optimal olmadığını mı merak ediyorum

    • Nokta matrisi aslında 5x8 karakterden 8 adet, yani toplam 320 bit; bu 320 biti kabuk protokolünde kullanılabilen bayt başına 4 bite paketliyorlar
      Buna 9 baytlık paket başlığı ve son eki ekleniyor
      Yazıda 92 yazmış gibi görünüyor, ama sanırım hesap hatası var
      7 bitin tamamını kullanmanın bir yolunu bulmak çok zor olduğu için, özgün yöntemle karşılaştırıldığında optimuma göre yalnızca çok az daha kötü bir çözüm seçilmiş
      Tam algoritmayı merak ediyorsanız, henüz düzenlenmemiş kod olsa da şu dosyalara bakabilirsiniz: https://github.com/portasynthinca3/swl01u/blob/master/fun/bi..., https://github.com/portasynthinca3/swl01u/blob/master/fun/ba...
  • “Tersine mühendislikte pek deneyimli değilim” sözünün karşılığı buysa, geri kalanımızın nerede durduğunu bilemiyorum

    • Çok şey bilip hiçbir şey bilmediğinin farkında olmak, deneyim 4. aşama civarı gibi
      1. aşama yeni ve hevesli olup henüz bilmediğini bilme hali, 2. aşama “Ben tanrıyım”, 3. aşama ise “Ben aptalım” aşaması
    • Özellikle çok iyi amatör mühendisler bu tür şeyleri sık söylüyor
  • “Dünyanın ilk MIDI shellcode’u” denmiş, ama büyük platformların çoğunda MIDI shellcode 20 yılı aşkın süredir vardı: https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=midi

    • Buffer overflow çok, ama o zafiyetler için gerçekten shellcode yazan biri olup olmadığı ayrı konu
  • Tabii ki SysEx’miş
    Standart MIDI’de SysEx, Python’daki inline assembler gibidir
    Neredeyse her MIDI cihazının içinde belgelenmemiş, tescilli şeyler saklıdır

    • Bir müzik parçası çalarak uzaktan kod yürütme tetiklenebilse güzel olurdu
      MIDI klavye takıp Am6,9/G# çalınca root yetkili bir terminal penceresi açılsa gerçekten harika olurdu
    • Google’ın son birkaç yıldır geliştirdiği Chrome MIDI desteğine ne tür SysEx hack’leri sıkıştıracağını merakla bekliyorum
    • Böyle bir dünyanın varlığından hiç haberim yoktu
      Yakın zamanda MIDI fuzzing yapmak için neler gerektiğini araştırdım; bunun için .mid dosyası üreten kaynaklar buldum ama istediğim şeyden biraz farklıydı
      Bunun yerine bu tarafı keşfetmeye değer görünüyor
    • SysEx harika
      Günümüz synthesizer’larının giderek daha az kullanıyor gibi olması epey üzücü, özellikle Roland
      Yine de Behringer hâlâ oldukça iyi destekliyor
      Örneğin Deepmind’in MIDI CC aralığı zaten iyi, ama SysEx ile neredeyse %100 programlanabiliyor
  • Şaşırtıcı bir araştırma
    Gerçek DNA/RNA moleküllerine shellcode sentezleyip DNA dizileme cihazında uzaktan kod yürütme gösteren 2017 tarihli çalışmayı biraz hatırlattı: https://www.usenix.org/conference/usenixsecurity17/technical...
    “Sırada OSC mi var?” diyecektim, ama şimdilik MIDI hâkim gibi görünüyor

  • Yazının tamamını okumayı öneririm, ama bence kilit cümleler şunlar
    “Bu [klavye üreticisi] delileri, USB üzerindeki MIDI SysEx mesajları üstünde çalışan bir kabuk yapmış”
    “En ilginç komut, rastgele bellek okuma/yazma komutudur. İsterseniz MIDI ile synthesizer belleğine bakıp onu kurcalayabilirsiniz”
    “İsterseniz bu mesajları bir MIDI dosyasına yazıp diğer MIDI dosyaları gibi synthesizer’da çalabilirsiniz. Hah, aklıma iyi bir fikir geldi…”
    “Firmware’in içine dalarak geçen sayısız uykusuz gecenin sonunda, LCD denetleyicisine rastgele veri gönderen bir fonksiyon buldum”

    • Şimdi asıl soru, klavyede çalışan kodu değiştirip aynı modelden başka bir klavye bu MIDI verisini aldığında ona bulaşmayı denetmesini sağlayıp sağlayamayacağımız
      Bir bakıma Nesnelerin İnterneti kâbusuna küçük bir bakış atmak gibi
      Neredeyse her cihazda bir arka kapı olabilir; hatta #0000 gibi aptalca bir arka kapı bile olabilir
    • Bunu bir MIDI dosyası gibi çalarsanız ortaya çıkan şey muhtemelen dubstep olur
    • MIDI ile synthesizer belleğini okuyup yazabilmek kulağa kolay geliyor, ama SysEx’te teslimat garantisi yok; bağlantı ya da oturum kavramı da yok, bu yüzden can sıkıcı olabilir
      Paket kaybı yaşanması tamamen normal
  • Bad Apple komutlarının arasına MIDI müziği sıkıştırıp sesin de kendi kendine çalmasını sağlayıp sağlayamayacaklarını merak ediyorum

  • Depo README’sinde imaj dökümü içerdiği yazıyor, ama gerçekte yok
    Bunun beklenen durum olup olmadığını merak ediyorum

    • Hata olmuş
      Son anda assemble edilmiş kod parçalarını hariç tutmak için *.bin’i .gitignore’a ekledim, sanırım döküm de onunla birlikte hariç kaldı
      Birkaç saat içinde yükleyeceğim
    • Öyle görünüyor
      Söz konusu dökümler Yamaha telif hakkı ile korunuyor olabilir; bu yüzden belki de daha iyi bir karar olmuştur
  • HN okurları arasında Ermenistan’da olan varsa, Porta 10 Ocak’ta Hacker Embassy’de bu konuda sunum yapacak
    Mutlaka uğramanızı öneririm: https://t.me/hackerembassy/17