จุดเริ่มต้นของการเดินทางจำลอง iOS 14 บน QEMU
- เดิมใช้โปรเจกต์โอเพนซอร์ส
alephsecurity/xnu-qemu-arm64 แต่เป็นแบบอ่านอย่างเดียว (read-only) จึงมีข้อจำกัดด้านการขยายต่อ
- ต่อมาเปลี่ยนมาใช้โปรเจกต์
TrungNguyen1909/qemu-t8030 ทำให้สามารถใช้ความสามารถต่อไปนี้ได้:
- ฟังก์ชันกู้คืน iOS (พร้อม QEMU อีกตัวสำหรับการเชื่อมต่อ USB)
- รัน iOS 14
- อิงกับ QEMU เวอร์ชันใหม่
- มีเอกสารวิกิแบบละเอียด
- แก้ไข
launchd.plist แล้วเข้าถึงเชลล์และ SSH ได้สำเร็จ จึงใช้เป็นจุดเริ่มต้นที่ดี
- เป้าหมายคือสร้างสภาพแวดล้อมจำลอง iOS ที่สมบูรณ์ ซึ่งสามารถแสดง UI และรันแอปได้
การแพตช์เคอร์เนลและการนำ PongoOS มาใช้
- โปรเจกต์
t8030 ใช้โครงสร้างที่แพตช์เคอร์เนลภายใน QEMU โดยตรง → ทำให้เกิดปัญหาด้านการบำรุงรักษาและการต่อยอด
- จากประสบการณ์ด้านการเจลเบรก จึงเปลี่ยนไปใช้โครงสร้างที่นำแพตช์
checkra1n มาใช้ผ่าน PongoOS
- เพิ่มขนาด SRAM ใน QEMU เพื่อรัน PongoOS และฉีดโมดูล checkra1n-KPF
- ระหว่างบูตเกิดปัญหา FPU ไม่ถูกตั้งค่า เพราะขาดฟังก์ชันบางส่วนของ bootrom/iboot → แก้ไขโดยอ้างอิงเอกสาร ARM
- หลัง A13 มีการนำ PAC (Pointer Authentication) มาใช้ ทำให้แพตช์บางส่วนใช้ไม่ได้อีก
- ใช้
task_for_pid0 (tfp0) เป็นตัวอย่างในการเปรียบเทียบไบนารีก่อนและหลังมี PAC
การพัฒนาเครื่องมืออัตโนมัติสำหรับแพตช์เคอร์เนล
- วิธีแพตช์แบบไดนามิกเดิมของ checkra1n อ่านยากและแก้ไขไม่สะดวก → จึงนำรูปแบบแพตช์เชิงประกาศที่อิงข้อความมาใช้
- เปรียบเทียบไบนารี
Mach-O สองไฟล์เพื่อดึงความต่างระดับแอสเซมบลี แล้วสร้างแพตช์ข้อความขึ้นมา
- บูตผ่าน Pongo แล้วดัมพ์หน่วยความจำเพื่อประกอบเคอร์เนลใหม่ → จัดระเบียบแพตช์ทั้งหมดเป็นไฟล์ข้อความพร้อมคอมเมนต์
การเรนเดอร์กราฟิก: Metal เทียบกับซอฟต์แวร์เรนเดอร์
- iOS ใช้ API
Metal ในการเรนเดอร์ UI ทั้งหมด → จึงต้องมี GPU
- เนื่องจากการจำลอง GPU ซับซ้อน จึงพิจารณาทางเลือกดังนี้:
- ซอฟต์แวร์เรนเดอร์
- ส่งต่อการเรียก Metal ไปยังอุปกรณ์จริงผ่านพร็อกซี
- ใน iOS 14 มีการเอา bootarg
gpu=0 ออกไป → วิเคราะห์ QuartzCore แล้วพบพฤติกรรม fallback
- แพตช์
QuartzCore บนเครื่องที่เจลเบรกแล้ว และยืนยันได้ว่าซอฟต์แวร์เรนเดอร์ทำงานได้ (แม้จะช้าแต่ทำได้จริง)
- มีการทดลองแนวทางพร็อกซี Metal ด้วย แต่หยุดไปเพราะความซับซ้อนของ Objective-C และ API
การดีบักเฟรมบัฟเฟอร์และ IOSurface
- QEMU ของ
t8030 ไม่มีการทำเฟรมบัฟเฟอร์ไว้ → จึงใช้ฟอร์ก ChefKissInc/QEMUAppleSilicon
- ตอนบูตช่วงแรกมองเห็นโลโก้ Apple และแถบความคืบหน้า แต่หลังจากนั้นจอกลายเป็นสีดำ → จึงเริ่มดีบัก
- จากการวิเคราะห์ IOMFB kext พบว่ามีอยู่สองโหมด:
- เฟรมบัฟเฟอร์ที่ใช้แอดเดรสคงที่ (สำหรับการแสดงผลช่วงต้น)
- โครงสร้างหลายเพลนแบบอิง DMA
- ระหว่างบูตระบบมีการใช้โหมดแบบอิง DMA → ใช้ trace ของ QEMU เพื่อตรวจสอบการตั้งค่ารีจิสเตอร์ของเคอร์เนล
- แต่ถึงอย่างนั้นก็ยังไม่มีภาพออกบนหน้าจอ
การปิดการสุ่มแอดเดรส
- การสุ่มแอดเดรสของเคอร์เนลสามารถปิดได้ในโค้ดเริ่มต้นของบอร์ด
- การสุ่มใน user space ถูกปิดโดยแพตช์
_load_machfile
- dyld cache เป็นไบนารีขนาดใหญ่ที่รวมไลบรารีไดนามิกทั้งหมดไว้ → ถูกโหลดที่แอดเดรสคงที่ตอนบูต
- สร้างเครื่องมือ C เพื่อ
dlopen แล้วตรวจสอบแอดเดรสผ่านฟังก์ชัน _dyld_*
- ทำให้สามารถดีบักไลบรารี
dyld ด้วย GDB ได้ → โดยเฉพาะ IOMFB, SpringBoard, QuartzCore
การเข้าถึงล็อกผ่าน USB และการข้าม lockdownd
- บนอุปกรณ์จริงสามารถเก็บล็อกระบบด้วย
idevicesyslog ได้ → แต่ต้องผ่านการยืนยันตัวตนผ่าน USB
- lockdownd ใช้ keybag ที่ต้องพึ่ง SEP สำหรับเก็บคีย์ → ซึ่งไม่มีในอีมูเลเตอร์
- จึงแทรกเชลล์โค้ดไว้แทนฟังก์ชันเดิม เพื่อให้โหลดจากไฟล์คีย์โดยตรง
- สามารถข้ามการยืนยันคีย์ระหว่าง QEMU ที่เชื่อมต่อผ่าน USB ได้สำเร็จ → จึงเก็บล็อกได้
- ยืนยันได้ว่า QuartzCore เริ่มทำงานตามปกติและใช้ซอฟต์แวร์เรนเดอร์อยู่
การข้าม PAC (Pointer Authentication)
- ระหว่างแก้ไข
backboardd พบข้อผิดพลาดเกี่ยวกับ PAC → เป็นฟีเจอร์ความปลอดภัยที่ถูกนำมาใช้ใน ARMv8.3
- วิธีแทนที่คำสั่ง PAC ด้วย NOP นั้นรุกล้ำระบบมากเกินไป
- คำสั่ง PAC สามารถคอมไพล์ในรูปแบบที่ยังเข้ากันได้ → หากให้ QEMU เพิกเฉยต่อ PAC ก็ยังรันได้
- QEMU 7 ไม่สามารถข้าม PAC ได้ → จึงย้ายไปใช้ QEMU 8.2.1
- ต้องย้ายโค้ดปรับแต่ง QEMU จำนวนมาก ทั้งคำสั่งเฉพาะของ Apple และ exception level ของ GL เป็นต้น
- ผลลัพธ์คือสามารถบูต iOS บน QEMU 8 ได้สำเร็จ และทำให้ PAC ใช้งานไม่ได้ในทางปฏิบัติ
การยืนยัน backboardd และเอาต์พุตกราฟิก
backboardd ทำงานอยู่แต่ยังไม่มีภาพแสดงบนหน้าจอ → อาจเกิดได้จากหลายสาเหตุ
- แม้ดัมพ์หน่วยความจำ DMA แล้วก็ยังไม่พบเอาต์พุตที่มีความหมาย
- ตรวจสอบแอดเดรสใน
iosurface_lock และดัมพ์เฟรมออกมา แต่ดูเหมือนถูกบีบอัดก่อนส่งไปยัง GPU
- บน iPhone X (t8015) พบเอาต์พุตที่ไม่ถูกบีบอัด → จึงแก้ DTB ของ QEMU เปลี่ยน
chip-id จาก t8030 เป็น t8015
- ผลคือหลังบูตสามารถแสดงโลโก้ Apple ได้
การติดตามแถบความคืบหน้าและข้อผิดพลาดของระบบ
- หลังโลโก้มีแถบความคืบหน้าสีขาวปรากฏขึ้น → แต่ค้างอยู่ที่ 90%
- จากการวิเคราะห์ล็อกพบปัญหาใน
mobileactivationd และ SpringBoardFoundation → หลังแพตช์แล้ว UI เปลี่ยนไป
- การแก้ปัญหาการค้างของแถบต้องอาศัยการวิเคราะห์ล็อกระบบจำนวนมาก
การทำแพตช์ dyld cache และ user space แบบอัตโนมัติ
- ใน user space ก็ใช้แนวทางแพตช์แบบข้อความเช่นเดียวกับเคอร์เนล
- dyld cache มีขนาด 2GB ทำให้แก้ไขตรง ๆ ไม่มีประสิทธิภาพ → จึงปรับปรุงเครื่องมือภายในให้สามารถ:
- ติดตามออฟเซ็ตภายใน dyld
- แพตช์ตำแหน่งเฉพาะได้โดยตรงด้วยคำสั่ง
dd
- จำเป็นต้องทำแพตช์เพื่อข้ามการตรวจสอบลายเซ็นเคอร์เนลควบคู่กันไปด้วย
การรัน PreBoard และการยืนยัน UI
- แอป
PreBoard เป็นแอประบบที่จะแสดงเมื่อเกิดข้อผิดพลาด → และสามารถสั่งรันได้โดยตรง
- เพิ่มเซิร์ฟเวอร์ VNC เพื่อพยายามปลดล็อกหน้าจอด้วยคีย์บอร์ด
- หลังปลดล็อก
vImage framework ใช้คำสั่ง AMX (Apple Matrix Coprocessor) → ซึ่ง QEMU ยังไม่รองรับ
- แพตช์ให้
vImage ใช้เส้นทาง fallback แบบซอฟต์แวร์ จึงแก้ปัญหาได้
- หลังแพตช์แล้วสามารถแสดงหน้าจอที่พิมพ์ข้อความได้สำเร็จ
บทสรุป
- ไปถึงจุดก่อนการรัน SpringBoard ได้แล้ว → จากนี้การรัน UI เต็มรูปแบบน่าจะเป็นเพียงเรื่องของเวลา
- มีการวิเคราะห์และแพตช์ทั้งเคอร์เนล, user space, กราฟิก, และฟีเจอร์ความปลอดภัย (เช่น PAC) จากหลายมิติ
- ยืนยันความเป็นไปได้ของสภาพแวดล้อมสำหรับดีบักและทดสอบแอป iOS บน QEMU ที่ใช้งานได้จริง
1 ความคิดเห็น
ความคิดเห็นบน Hacker News