1 คะแนน โดย GN⁺ 2024-01-23 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • TheZZAZZGlitch แสดงให้เห็นว่าสามารถบันทึก เสียงแครชของ Game Boy Advance เพื่อระบุข้อมูลเกมภายในคาร์ทริดจ์ และสุดท้ายกู้คืน ROM เดียวกันได้
  • หัวใจสำคัญคือการตีความเสียงที่ถูกส่งออกมาหลังเกิดแครชเป็น ข้อมูล ROM แต่ต้องปรับแต่งตามรูปแบบของแหล่งข้อมูลค่อนข้างมาก จึงยากที่จะใช้เป็นเครื่องมือดัมป์แบบทั่วไป
  • ในการบันทึกนานกว่า 4 ชั่วโมง พบรูปคลื่นที่มีลักษณะเฉพาะที่ประมาณ จุด 1 ชั่วโมง 50 นาที จากนั้นจึงได้ยินเสียงเครื่องดนตรีและตัวอย่างเสียงของเกมตามลำดับ
  • ด้วยสคริปต์ Python และการแก้ไขการจัดแนว ทำได้ถึง ความแม่นยำ 99.76% แต่ยังบูตไม่สำเร็จ และเมื่อรวมการบันทึก 3 ครั้งด้วยอัลกอริทึมเสียงข้างมาก ก็ปรับปรุงขึ้นเป็น 99.979%
  • หลังรวมการบันทึก 7 ชุดและกรองพื้นที่ว่างออก จึงทำได้ ตรงกัน 100% ยืนยันความเป็นไปได้เชิงทดลองว่าสามารถกู้คืน ROM ของ GBA ได้ด้วยเสียงแครชเพียงอย่างเดียว

การทดลองอ่านข้อมูล ROM จากเสียงแครช

  • TheZZAZZGlitch สาธิตว่าเสียงที่ GBA ส่งออกมาหลังซอฟต์แวร์แครชอาจมีข้อมูลเกมรวมอยู่ด้วย
  • GBA ที่แครชสามารถส่งเสียงต่อไปโดยอ้างอิงข้อมูลภายในคาร์ทริดจ์ได้ และหากวิเคราะห์เสียงนี้ด้วยฮาร์ดแวร์พิเศษและโค้ด ก็สามารถระบุได้ว่าเป็นเกมใด
  • อย่างไรก็ตาม วิธีนี้ไม่ใช่วิธีดัมป์ข้อมูลคาร์ทริดจ์แบบง่าย ๆ และไม่ใช่ โซลูชันที่พร้อมใช้งานทันที
    • ต้องมีการจูนจำนวนมากให้เหมาะกับรูปแบบแหล่งข้อมูลที่แตกต่างกัน

รูปคลื่นที่เผยตัวในการบันทึกยาว

  • หลังทำให้ GBA แครชแล้วบันทึกเสียงไว้นาน มากกว่า 4 ชั่วโมง พบรูปคลื่นที่มีลักษณะเฉพาะที่ประมาณช่วง 1 ชั่วโมง 50 นาที
  • ในช่วงถัดมา ได้ยิน เสียงเครื่องดนตรีและตัวอย่างเสียง จริงที่อยู่ในเกมตามลำดับ
  • ข้อมูลส่วนที่เหลือฟังเหมือน ข้อมูล 8 บิต ที่ 13,100Hz และบางช่วงฟังดูแปลกมาก

สคริปต์ Python และความพยายามกู้คืนครั้งแรก

  • TheZZAZZGlitch เตรียม สคริปต์ Python สำหรับอ่านเสียงบันทึกดัมป์จาก GBA ที่แครชแบบสะอาด หลังจาก “แก้บั๊กอยู่ 2 วัน”
  • ในข้อมูล ROM มีช่วงที่เป็นไบต์ 0 อยู่เป็นจำนวนมาก และส่วนนี้ปรากฏเหมือนความเงียบ ทำให้พาร์สจากเสียงได้ยาก
  • หลังรันสคริปต์อีกตัวเพื่อจัดเรียงเซ็กชันใหม่ตามตำแหน่งใน ROM ต้นฉบับ ROM ที่กู้คืนได้มี ความแม่นยำ 99.76%
  • ROM นี้ยังบูตไม่ได้ และเนื่องจากใช้ข้อมูล ROM ที่รู้จักอยู่แล้วเพื่อเผยข้อมูลที่ไม่รู้ จึงถือว่าในเชิงเทคนิคเป็น “การโกง”
    • ถึงแม้จะทำแบบ blind ทั้งหมด ก็ยังมีสมมติฐานและการคาดเดาบางอย่างที่นำมาใช้ได้

รวมการบันทึกหลายชุดเพื่อเพิ่มความแม่นยำ

  • ขั้นต่อไปมุ่งเน้นที่ การปรับปรุงคุณภาพการบันทึก
  • เมื่อนำผลจากการบันทึก 3 ครั้งมารวมด้วยอัลกอริทึม “เสียงข้างมาก” ความแม่นยำเพิ่มขึ้นเป็น 99.979%
  • ROM เอาต์พุตนี้บูตได้สำเร็จ แต่ข้อความเพี้ยนและเกิดแครชที่หน้าจอไตเติล
  • จากนั้นเมื่อรวมการบันทึก 7 ชุดและกรองพื้นที่ว่างออก ก็ทำได้ ตรงกัน 100%

ฮาร์ดแวร์จริงและการทดลองเพิ่มเติม

  • ช่วงท้ายของวิดีโอมีการตรวจสอบด้วยว่าวิธีนี้ทำงานบน ฮาร์ดแวร์จริง อย่างไร
  • ยังทดลองกับเกมอื่น ๆ และติดตามปริศนาที่เกี่ยวข้องกับโค้ด ARM ภายในคาร์ทริดจ์โคลนด้วย
  • มีการลองวิธีต่าง ๆ เพื่อให้ได้การบันทึกที่ดีกว่าเดิม
    • หนึ่งในนั้นคือการใช้ “cursed adapter” ที่มิกซ์ดาวน์แบบหยาบ ๆ ลงเป็นช่องสัญญาณเดียว

1 ความคิดเห็น

 
GN⁺ 2024-01-23
ความคิดเห็นใน Hacker News
  • ปัญหาเมื่อมี 0x00 ต่อเนื่องยาว ๆ เกี่ยวข้องกับ การกู้คืนสัญญาณนาฬิกา (clock recovery)
    สตรีมข้อมูลดิจิทัลบางประเภท โดยเฉพาะข้อมูลดิบจากหัวอ่านแม่เหล็กของดิสก์ไดรฟ์ หรือการสื่อสารอนุกรมความเร็วสูงอย่าง Ethernet จะถูกส่งโดยไม่มีสัญญาณนาฬิกาแยกต่างหาก
    ตัวรับจะสร้างสัญญาณนาฬิกาจากความถี่อ้างอิงคร่าว ๆ แล้วใช้วงจรล็อกเฟส (PLL) ปรับเฟสของสัญญาณนาฬิกาให้ตรงกับการเปลี่ยนสถานะในสตรีมข้อมูล
    เพื่อให้วิธีนี้ทำงานได้ การเปลี่ยนสถานะของข้อมูลต้องเกิดบ่อยพอที่จะชดเชยการ drift ของออสซิลเลเตอร์ใน PLL ได้ และสเปกที่บอกว่าทนได้นานแค่ไหนเมื่อไม่มีการเปลี่ยนสถานะเรียกว่า จำนวนตัวเลขเดียวกันต่อเนื่องสูงสุด (CID)
    https://en.wikipedia.org/wiki/Clock_recovery

    • เมื่อก่อนข้อดีคือใช้ วิธี codebook อันชาญฉลาดที่จัดการช่วงบิตสั้น ๆ อย่างพิถีพิถัน เช่น 8b/10b เพื่อรับประกันว่าสามารถกู้คืนสัญญาณนาฬิกาได้และหลีกเลี่ยงปัญหาความจุไฟฟ้าของสายสัญญาณ
      ต่อมาจึงเปลี่ยนไปเป็นวิธีอย่าง 64/66b ที่นำกลุ่มบิตขนาดใหญ่มาใส่ header สั้น ๆ เพื่อรับประกันการเปลี่ยนสถานะของสัญญาณนาฬิกา แล้วส่งทั้งหมดผ่าน pseudo-random scrambler
    • อีกเรื่องที่น่ากังวลคือถ้าบางจุดของรูปคลื่นถูก คัปปลิงแบบ AC (AC coupling) ทำให้กระแสตรงผ่านไม่ได้
      แม้สัญญาณนาฬิกาจะซิงก์กันอย่างสมบูรณ์ แต่ในช่วงยาว ๆ สัญญาณที่เกือบทั้งหมดเป็น 1 กับสัญญาณที่เกือบทั้งหมดเป็น 0 ก็จะกลายเป็นเหมือนกันในที่สุด
    • กรณีนี้ดูไม่น่าเกี่ยวข้องเท่าไร
      เสียงเป็นอนาล็อก และหากจะถอดรหัสเป็น bitstream ก็ต้องมี DAC โดยถูกส่งที่ความถี่คงที่อย่าง 44.1kHz หรือ 48kHz โดยไม่มีการซิงโครไนซ์พิเศษ
    • เจอบทความที่น่าสนใจมากชื่อ Wireless Set Number 10 ในหน้านี้
      https://en.wikipedia.org/wiki/Wireless_Set_Number_10
  • นึกถึงการแฮ็ก iPodLinux ดั้งเดิมเมื่อเกือบ 20 ปีก่อน ที่ dump เฟิร์มแวร์ iPod รุ่นที่ 4 ผ่าน ลำโพง piezo
    https://web.archive.org/web/20140810083116/http://www.newscientist.com/article/dn7085
    บังเอิญว่านั่นทำให้เล่นเกม GBA บน iPod ได้ด้วย แต่แค่ได้เล่น Doom ด้วย click wheel และดูวิดีโอขาวดำก็พอใจมากแล้ว

    • เป็นความทรงจำที่ดี
      ยังมี iPod Classic อยู่ และหลังอัปเกรดแบตเตอรี่ก็ใส่การ์ด MicroSD 256GB ไป 4 ใบ
      ช่วงหลังเห็นม็อดที่เพิ่ม Bluetooth ได้ด้วย แต่ต้องเปลี่ยนฝาหลัง เลยคงไม่ไปไกลถึงขั้นนั้น
      ทั้งดีไซน์และความรู้สึกของการได้เป็นเจ้าของสำเนาไฟล์เพลงด้วยตัวเองนั้นมี ความรู้สึกที่ไม่จางหายไปตามกาลเวลา
  • ดีใจที่เรื่องนี้ได้รับความสนใจมากขึ้นที่นี่
    เคยถูกโพสต์ไปเมื่อไม่กี่วันก่อนแต่จมหายไป: https://news.ycombinator.com/item?id=39037104
    ในวิดีโอต้นฉบับมีเนื้อหาอีกมากที่บทความสั้นนี้ไม่มี รวมถึง อะแดปเตอร์สั่งทำ ที่แฮ็กเกอร์ตัดต่อประกอบเองเพื่อให้ได้คุณภาพเสียงที่เหมาะสมจาก DS

  • Zzazz จัด การแข่งขัน/อีเวนต์วัน April Fools' Day ทุกปี และโดยปกติมักมีการแฮ็กย้อนยุคหรือ reverse engineering อยู่พอสมควร
    แนะนำให้เข้าร่วม
    การแข่งขันครั้งก่อน ๆ อยู่บน GitHub

  • สถาปัตยกรรมเสียงของ Nintendo น่าสนใจเสมอ
    เดิมที NES มีตัวสร้าง sample ที่สามารถสร้างรูปคลื่นแบบกำหนดเองได้ และขับได้สองวิธี
    วิธีหนึ่งคือส่ง address หน่วยความจำให้ แล้วมันจะอ่านบิตและประมวลผลเป็นรูปคลื่นที่เรียบง่ายมาก โดย 1 คือ “เพิ่มค่าขึ้นหนึ่ง” และ 0 คือ “ลดค่าลงหนึ่ง” ดังนั้นถ้าต้องการทำรูปคลื่นแบน ๆ ก็ต้องทำซ้ำแบบ 10101010
    อีกวิธีคือให้ CPU ป้อน “ค่าเริ่มต้น” เข้าไปเรื่อย ๆ เพื่อขับชิปโดยตรง ซึ่งในทางปฏิบัติเร็วกว่าการให้ audio driver อ่านบิตจาก RAM
    ปัญหาคือวิธีนี้กิน CPU cycle ทั้งหมด จึงใช้ได้เฉพาะตอนที่ไม่ได้ทำอย่างอื่น
    เกมอย่าง Battletoads ใช้วิธีนี้เล่นเสียงกลองคุณภาพสูงขึ้นในช่วงที่แอ็กชันหยุดอยู่ และใช้ในหน้าจอ title, เอฟเฟกต์ “กรอบ” ตอนที่แอ็กชันทั้งหมดหยุดชั่วครู่เมื่อโจมตีศัตรูครั้งสุดท้าย รวมถึงเพลง pause ที่น่าจดจำ
    มีเดโมที่แสดงให้เห็นเกมสลับระหว่าง โหมดขับโดยตรง กับโหมดที่ชิปอ่าน sample อยู่ที่นี่ Retro Game Audio โพสต์วิดีโอที่แก้ด้วย emulator เพื่อแสดงจังหวะที่เข้าสู่ subroutine ขับโดยตรง: https://www.youtube.com/watch?v=JGT0FM3yh-w

  • สงสัยตั้งแต่แรกเลยว่าทำไมเรื่องแบบนี้ถึงเกิดขึ้นได้
    เกมพวกนี้ดัมป์สถานะออกมาเป็นเสียงกันบ่อยไหม? หรือเป็น เครื่องมือดีบัก ที่ตั้งใจทำไว้สำหรับนักพัฒนาเกม?

    • วิดีโอก่อนหน้าของผู้เขียน[1] อธิบายรายละเอียดเชิงเทคนิคของพฤติกรรมนี้ไว้
      โดยพื้นฐานแล้ว เสียงของ GBA จะสตรีมออดิโอจากบัฟเฟอร์ใน RAM และอินเทอร์รัปต์ต้องบอกฮาร์ดแวร์ให้อ่านใหม่ตั้งแต่ต้นบัฟเฟอร์
      แต่ถ้าไม่มีอินเทอร์รัปต์เกิดขึ้น เช่นตอนที่เกมแครช สตรีมเสียงก็จะอ่านเลยบัฟเฟอร์ไปยังพื้นที่อื่นของหน่วยความจำ
      [1] https://www.youtube.com/watch?v=wSWNkpqjtQY&t=361s
    • เป็นเรื่องค่อนข้างหายาก และไม่ใช่เครื่องมือดีบักที่ตั้งใจทำไว้ด้วย
      ในทางทฤษฎี GBA อาจทำให้สถาปัตยกรรม “หยุดนิ่ง” เมื่อเกมแครช หรือใส่ watchdog ที่ผูกการอัปเดตสถานะซึ่งควรรันเป็นระยะไว้กับ non-maskable interrupt แล้วรีบูตถ้าการอัปเดตนั้นหยุดลงก็ได้
      แต่ฟีเจอร์แบบนั้นมีต้นทุนเพิ่ม GBA เลยไม่มี
      Nintendo จึงเลือกแนวทางดั้งเดิมของผู้ผลิตตลับเกมยุคเก่า กล่าวคือ “ถ้าเกมของเราไม่มีบั๊ก เราก็ไม่ต้องกังวลว่าฮาร์ดแวร์ในสถานะที่ไม่ได้นิยามไว้จะทำงานอย่างไร”
      ดังนั้นเมื่อเกม GBA ตกอยู่ในสถานะแครช เช่นลูปไม่สิ้นสุดที่ปิดอินเทอร์รัปต์ ชิปเสียงก็ไม่รู้ว่าระบบแครชแล้ว และยังคงทำงานง่าย ๆ คืออ่านบิตต่อเนื่องจาก RAM แล้วแปลงเป็นเสียงต่อไป
      เมื่อรูทีนจัดการที่คอยดูแลการอ่านในภาวะปกติหายไป มันก็อ่านต่อเรื่อย ๆ จนสุดท้ายไปถึงบิตที่แทนค่าของ ROM ในตลับ
  • น่าประทับใจแบบเหลือเชื่อจริง ๆ
    เทคนิคอย่าง อัลกอริทึมเสียงข้างมาก ที่ใช้ในนี้น่าจะยังถูกใช้งานน้อยเกินไปในหลายอุตสาหกรรม

    • ถ้าสนใจ เทคนิค การกู้คืนสัญญาณที่มีสัญญาณรบกวน แบบซับซ้อนนั้นสะสมกันมาเกือบ 100 ปีแล้ว
      สื่อบันทึกข้อมูลแบบแม่เหล็กโดยพื้นฐานก็ทำงานด้วยหลักการเดียวกับแฮ็กนี้
      https://en.wikipedia.org/wiki/Partial-response_maximum-likelihood
      แนวคิดเดียวกันนี้นำมาใช้ที่นี่ได้ด้วย เพราะเนื้อหาใน GBA ROM น่าจะมีอคติของข้อมูลค่อนข้างแรง
      วิธีโหวตเสียงข้างมากทิ้งข้อมูลไปเยอะมาก
    • แอปพลิเคชันที่น่าสนใจของการเลือกค่าที่พบมากที่สุด หรือโดยทั่วไปคือเลือก ค่ามัธยฐาน คือการลบนอยส์หรือคนออกจากภาพถ่ายหลายภาพที่ถ่ายในเวลาเดียวกัน
      แค่วางภาพทั้งหมดซ้อนกัน แล้วสำหรับแต่ละพิกเซลให้เหลือเฉพาะค่ามัธยฐาน[1]
      [1]: https://patdavid.net/2013/05/noise-removal-in-photos-with-median_6/
    • ในงานกู้ข้อมูลถือว่าค่อนข้างพบบ่อย
      ประมาณว่าอ่านดิสก์อิมเมจซ้ำ ๆ แล้วใช้การตัดสินแบบ quorum จนได้ผลลัพธ์ที่ผ่าน checksum จากนั้นค่อยดูว่าทำงานถูกต้องไหม
      ถ้ามี checksum ระดับสูงกว่า เช่น file signature ก็ยิ่งดีสำหรับการตรวจสอบเพิ่มเติม
      ในงานอวกาศบางประเภทก็ใช้ด้วย
    • เคยคิดว่าน่าจะใช้กับการสแกนฟิล์มได้ โดยเฉพาะโปรเจกต์แฟน ๆ อย่าง 4K77
      เพราะไม่ได้ทำงานกับมาสเตอร์ที่ pristine แต่เป็นฟิล์มฉายโรงที่อาจเสียหายได้ ถ้าใช้หลายชุดเพื่อลบรอยขีดข่วนและอย่างอื่นได้ ก็น่าจะลดเวลาการแก้ไขด้วยมือในขั้น post ได้มหาศาล
  • การที่ 0xFF กลายเป็น 0x00 อาจเป็นเพราะ ตัวเก็บประจุบล็อกกระแสตรง หรือการกรองแบบ high-pass
    วงจรเสียงไม่ค่อยเหมาะกับคอนเทนต์ที่มนุษย์ไม่ได้ยิน
    ถ้าโชคดี การต่อดิจิทัลออสซิลโลสโคปเข้ากับเอาต์พุตเสียงของชิปโดยตรงอาจช่วยให้จับข้อมูลได้แม่นยำขึ้น แต่การได้อิมเมจที่บูตได้ออกมาก็ยังน่าประทับใจมากอยู่ดี

    • จำได้ว่าอ่านจากคอมเมนต์ในวิดีโอ YouTube ว่าเป้าหมายคือพยายามทำด้วย อุปกรณ์พื้นฐาน ให้มากที่สุด
      เลยใช้เวลาหลายชั่วโมงจับซ้ำหลายรอบแล้วเฉลี่ยข้อผิดพลาด แทนที่จะใช้ data-logging oscilloscope ดี ๆ
    • ใช่ ต้องสร้าง สัญญาณที่ไม่มี DC bias
      แค่ใช้วิธีง่าย ๆ อย่าง Manchester encoding ก็ช่วยได้มาก
      ถ้ายังไม่พอ ก็ใช้ NRZ หรือแม้แต่ convolutional encoding ได้
      นอกจากนี้ควรส่งคลื่นไซน์ หรือถ้าทำไม่ได้ อย่างน้อยก็เพิ่มความถี่ของคลื่นสี่เหลี่ยมให้สูงพอที่จะไม่ถูกตัวเก็บประจุแบบ AC coupling กลืนไป
  • ส่วนที่น่าประทับใจที่สุดในทั้งหมดนี้สำหรับผมคือ วิธีที่พวกทำเกมเถื่อนแก้โค้ดให้เกมรันจากแฟลชที่เขียนได้ แทนที่จะใช้ ROM+หน่วยความจำเก็บข้อมูลแบบ volatile

    • เป็นเทคนิคที่พบบ่อยมากในเกม Game Boy และ Game Boy Advance เถื่อน เพื่อประหยัดค่าแบตเตอรี่ไม่กี่เซนต์
      ที่จริงการทำแพตช์แบบนี้ก็ไม่ได้ยากขนาดนั้น
      พวกทำเกมเถื่อนจะ reverse engineer ตำแหน่งที่โค้ดเกมใช้บันทึกข้อมูลเล็กน้อย แล้วเขียนแพตช์เฉพาะเกมเพื่อ flush ข้อมูลเซฟไปยังแฟลชที่เขียนได้
      แต่เกม GBA อย่างเป็นทางการจะใช้ฟังก์ชันของ Nintendo SDK ในการเซฟเสมอ ดังนั้นถ้า hook ฟังก์ชันเหล่านั้น ก็จะทำ แพตช์อเนกประสงค์ ที่ทำให้เกม GBA ใด ๆ ก็เซฟบนตลับก๊อปที่ไม่มีแบตเตอรี่ได้ค่อนข้างง่าย
      ผมเขียนแพตเชอร์ที่ทำสิ่งนี้ไว้ ดูได้ที่นี่
      https://github.com/metroid-maniac/gba-auto-batteryless-patcher
  • มีใครรู้ไหมว่าเมื่ออีมูเลเตอร์ของ TheZZAZZGlitch รายงานว่าเกมพยายามกระโดดไปยังแอดเดรสที่ไม่ถูกต้อง ภายในเกิดอะไรขึ้น?
    ผมไม่คุ้นกับโปรเซสเซอร์ ARM7 ที่ใช้ใน GameBoy Advance แต่ก็นึกไม่ออกว่าการประกอบ jump call ด้วยค่าที่ไม่ถูกต้องเป็นไปได้อย่างไร
    แล้วก็สงสัยด้วยว่าถ้าเอา ROM ที่ TheZZAZZGlitch กู้คืนผิดหนึ่งตัวไปรันบน GameBoy จริง ๆ จะเกิดอะไรขึ้น

    • คำสั่ง bx ทำให้กระโดดไปยัง แอดเดรสใดก็ได้ ที่เก็บอยู่ในรีจิสเตอร์ได้
      และถ้ารัน ROM ที่กู้คืนผิดบน GameBoy จริง มันจะแครช แล้วสุดท้ายก็จะเริ่มเล่น ROM ออกทางลำโพง
      นั่นแหละคือประเด็นหลักของวิดีโอ :)
    • เป็นไปได้ว่าอีมูเลเตอร์จำลองเฉพาะการเข้าถึงพื้นที่ที่ถูกต้องใน memory map ของ GBA และถ้ามีการเข้าถึงพื้นที่ที่ไม่ถูกต้องก็โยน error นั้นออกมา
      ส่วนบนฮาร์ดแวร์จริงจะเกิดอะไรขึ้น… ใครจะไปรู้ :)