ม็อดเดอร์กู้คืน ROM เกมจากเสียงแครชของ GBA
(arstechnica.com)- 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 ความคิดเห็น
ความคิดเห็นใน Hacker News
ปัญหาเมื่อมี 0x00 ต่อเนื่องยาว ๆ เกี่ยวข้องกับ การกู้คืนสัญญาณนาฬิกา (clock recovery)
สตรีมข้อมูลดิจิทัลบางประเภท โดยเฉพาะข้อมูลดิบจากหัวอ่านแม่เหล็กของดิสก์ไดรฟ์ หรือการสื่อสารอนุกรมความเร็วสูงอย่าง Ethernet จะถูกส่งโดยไม่มีสัญญาณนาฬิกาแยกต่างหาก
ตัวรับจะสร้างสัญญาณนาฬิกาจากความถี่อ้างอิงคร่าว ๆ แล้วใช้วงจรล็อกเฟส (PLL) ปรับเฟสของสัญญาณนาฬิกาให้ตรงกับการเปลี่ยนสถานะในสตรีมข้อมูล
เพื่อให้วิธีนี้ทำงานได้ การเปลี่ยนสถานะของข้อมูลต้องเกิดบ่อยพอที่จะชดเชยการ drift ของออสซิลเลเตอร์ใน PLL ได้ และสเปกที่บอกว่าทนได้นานแค่ไหนเมื่อไม่มีการเปลี่ยนสถานะเรียกว่า จำนวนตัวเลขเดียวกันต่อเนื่องสูงสุด (CID)
https://en.wikipedia.org/wiki/Clock_recovery
ต่อมาจึงเปลี่ยนไปเป็นวิธีอย่าง 64/66b ที่นำกลุ่มบิตขนาดใหญ่มาใส่ header สั้น ๆ เพื่อรับประกันการเปลี่ยนสถานะของสัญญาณนาฬิกา แล้วส่งทั้งหมดผ่าน pseudo-random scrambler
แม้สัญญาณนาฬิกาจะซิงก์กันอย่างสมบูรณ์ แต่ในช่วงยาว ๆ สัญญาณที่เกือบทั้งหมดเป็น 1 กับสัญญาณที่เกือบทั้งหมดเป็น 0 ก็จะกลายเป็นเหมือนกันในที่สุด
เสียงเป็นอนาล็อก และหากจะถอดรหัสเป็น bitstream ก็ต้องมี DAC โดยถูกส่งที่ความถี่คงที่อย่าง 44.1kHz หรือ 48kHz โดยไม่มีการซิงโครไนซ์พิเศษ
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
https://www.youtube.com/watch?v=0-7PSmYYHF0
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
สงสัยตั้งแต่แรกเลยว่าทำไมเรื่องแบบนี้ถึงเกิดขึ้นได้
เกมพวกนี้ดัมป์สถานะออกมาเป็นเสียงกันบ่อยไหม? หรือเป็น เครื่องมือดีบัก ที่ตั้งใจทำไว้สำหรับนักพัฒนาเกม?
โดยพื้นฐานแล้ว เสียงของ GBA จะสตรีมออดิโอจากบัฟเฟอร์ใน RAM และอินเทอร์รัปต์ต้องบอกฮาร์ดแวร์ให้อ่านใหม่ตั้งแต่ต้นบัฟเฟอร์
แต่ถ้าไม่มีอินเทอร์รัปต์เกิดขึ้น เช่นตอนที่เกมแครช สตรีมเสียงก็จะอ่านเลยบัฟเฟอร์ไปยังพื้นที่อื่นของหน่วยความจำ
[1] https://www.youtube.com/watch?v=wSWNkpqjtQY&t=361s
ในทางทฤษฎี GBA อาจทำให้สถาปัตยกรรม “หยุดนิ่ง” เมื่อเกมแครช หรือใส่ watchdog ที่ผูกการอัปเดตสถานะซึ่งควรรันเป็นระยะไว้กับ non-maskable interrupt แล้วรีบูตถ้าการอัปเดตนั้นหยุดลงก็ได้
แต่ฟีเจอร์แบบนั้นมีต้นทุนเพิ่ม GBA เลยไม่มี
Nintendo จึงเลือกแนวทางดั้งเดิมของผู้ผลิตตลับเกมยุคเก่า กล่าวคือ “ถ้าเกมของเราไม่มีบั๊ก เราก็ไม่ต้องกังวลว่าฮาร์ดแวร์ในสถานะที่ไม่ได้นิยามไว้จะทำงานอย่างไร”
ดังนั้นเมื่อเกม GBA ตกอยู่ในสถานะแครช เช่นลูปไม่สิ้นสุดที่ปิดอินเทอร์รัปต์ ชิปเสียงก็ไม่รู้ว่าระบบแครชแล้ว และยังคงทำงานง่าย ๆ คืออ่านบิตต่อเนื่องจาก RAM แล้วแปลงเป็นเสียงต่อไป
เมื่อรูทีนจัดการที่คอยดูแลการอ่านในภาวะปกติหายไป มันก็อ่านต่อเรื่อย ๆ จนสุดท้ายไปถึงบิตที่แทนค่าของ ROM ในตลับ
น่าประทับใจแบบเหลือเชื่อจริง ๆ
เทคนิคอย่าง อัลกอริทึมเสียงข้างมาก ที่ใช้ในนี้น่าจะยังถูกใช้งานน้อยเกินไปในหลายอุตสาหกรรม
สื่อบันทึกข้อมูลแบบแม่เหล็กโดยพื้นฐานก็ทำงานด้วยหลักการเดียวกับแฮ็กนี้
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 ก็ยิ่งดีสำหรับการตรวจสอบเพิ่มเติม
ในงานอวกาศบางประเภทก็ใช้ด้วย
เพราะไม่ได้ทำงานกับมาสเตอร์ที่ pristine แต่เป็นฟิล์มฉายโรงที่อาจเสียหายได้ ถ้าใช้หลายชุดเพื่อลบรอยขีดข่วนและอย่างอื่นได้ ก็น่าจะลดเวลาการแก้ไขด้วยมือในขั้น post ได้มหาศาล
การที่ 0xFF กลายเป็น 0x00 อาจเป็นเพราะ ตัวเก็บประจุบล็อกกระแสตรง หรือการกรองแบบ high-pass
วงจรเสียงไม่ค่อยเหมาะกับคอนเทนต์ที่มนุษย์ไม่ได้ยิน
ถ้าโชคดี การต่อดิจิทัลออสซิลโลสโคปเข้ากับเอาต์พุตเสียงของชิปโดยตรงอาจช่วยให้จับข้อมูลได้แม่นยำขึ้น แต่การได้อิมเมจที่บูตได้ออกมาก็ยังน่าประทับใจมากอยู่ดี
เลยใช้เวลาหลายชั่วโมงจับซ้ำหลายรอบแล้วเฉลี่ยข้อผิดพลาด แทนที่จะใช้ data-logging oscilloscope ดี ๆ
แค่ใช้วิธีง่าย ๆ อย่าง Manchester encoding ก็ช่วยได้มาก
ถ้ายังไม่พอ ก็ใช้ NRZ หรือแม้แต่ convolutional encoding ได้
นอกจากนี้ควรส่งคลื่นไซน์ หรือถ้าทำไม่ได้ อย่างน้อยก็เพิ่มความถี่ของคลื่นสี่เหลี่ยมให้สูงพอที่จะไม่ถูกตัวเก็บประจุแบบ AC coupling กลืนไป
ส่วนที่น่าประทับใจที่สุดในทั้งหมดนี้สำหรับผมคือ วิธีที่พวกทำเกมเถื่อนแก้โค้ดให้เกมรันจากแฟลชที่เขียนได้ แทนที่จะใช้ ROM+หน่วยความจำเก็บข้อมูลแบบ volatile
ที่จริงการทำแพตช์แบบนี้ก็ไม่ได้ยากขนาดนั้น
พวกทำเกมเถื่อนจะ 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 ออกทางลำโพง
นั่นแหละคือประเด็นหลักของวิดีโอ :)
ส่วนบนฮาร์ดแวร์จริงจะเกิดอะไรขึ้น… ใครจะไปรู้ :)