4 điểm bởi GN⁺ 2025-04-07 | 1 bình luận | Chia sẻ qua WhatsApp
  • eShard đặt mục tiêu xây dựng một trình giả lập có thể khởi động iOS 14 trên QEMU, hiển thị UI và chạy được một số ứng dụng, dựa trên các nỗ lực mô phỏng iOS mã nguồn mở trước đây
  • Thay vì đưa trực tiếp các bản vá kernel vào bên trong QEMU, họ dùng PongoOS và checkra1n KPF để tách riêng các bản vá XNU, đồng thời tạo công cụ dựa trên diff Mach-O để có thể rà soát nội dung bản vá
  • Vì phạm vi mô phỏng GPU Apple Silicon quá lớn, họ trước tiên chọn render bằng phần mềm, và xác nhận qua bản vá QuartzCore trên iPhone jailbreak rằng UI UIKit có thể được vẽ, dù chậm
  • Để giải quyết vấn đề màn hình vẫn đen, họ tiếp tục tắt ngẫu nhiên hóa địa chỉ, debug bằng GDB, vượt qua cơ chế pairing của lockdownd, tắt PAC, port sang QEMU 8.2.1 và tự động hóa việc vá dyld cache
  • Cuối cùng, họ hiển thị được UI nhập mật mã của UIKit trên màn hình QEMU và thao tác textbox bằng đầu vào bàn phím qua VNC, đồng thời có được nền tảng cần thiết để hiển thị SpringBoard

Điểm khởi đầu của mô phỏng iOS

  • Khi xem xét các giải pháp mã nguồn mở hiện có, alephsecurity/xnu-qemu-arm64 từng chạy được trong trải nghiệm thực tế, nhưng dự án đã ở trạng thái chỉ đọc
  • Sau đó, TrungNguyen1909/qemu-t8030 được dùng làm điểm xuất phát
    • Có thể khôi phục iOS thông qua kết nối USB bằng QEMU “companion” thứ hai
    • Hỗ trợ chạy iOS 14
    • Dựa trên phiên bản QEMU mới hơn
    • Cung cấp wiki hướng dẫn cách chạy trình giả lập
  • Bằng cách chỉnh sửa System/Library/xpc/launchd.plist, họ nhanh chóng có được quyền truy cập shell và SSH
  • Mục tiêu dài hạn là mô phỏng iOS có tính chức năng, có UI và có thể chạy tối thiểu một số ứng dụng

Tách bản vá kernel bằng PongoOS

  • Dự án t8030 đưa mã vá kernel XNU vào chính QEMU, nhưng vì số lượng bản vá có thể tăng trong tương lai, cần một cấu trúc gọn gàng hơn
  • Dựa trên kinh nghiệm với iPhone jailbreak thật, họ xem xét cách dùng PongoOS để áp dụng các bản vá checkra1n
  • Trong luồng jailbreak thông thường, sau khi bị pwn bằng checkmate, PongoOS được inject vào SRAM và module checkra1n-kpf được truyền qua USB
    • Trong công việc này, để tránh xử lý USB ban đầu, họ tăng SRAM của iPhone được mô phỏng và dùng PongoOS cùng module checkra1n KPF
  • Ở giai đoạn đầu khi chạy PongoOS, phát sinh vấn đề vì không có mã khởi tạo mà bootrom hoặc iBoot thường thực hiện
    • Cần thiết lập FPU trước các lệnh double/float
    • Họ giải quyết bằng cách tham khảo tài liệu ARM và mã liên quan đến QEMU hiện có
  • Pongo không hỗ trợ các tính năng của thiết bị từ A13 trở đi, làm hỏng việc khớp mẫu của một số bản vá
    • Có thêm các lệnh Pointer Authentication (PAC) như autda, xpacd
    • Apple dùng slide khác
    • Ở bản vá task_for_pid(tfp0), xác nhận có khác biệt về địa chỉ và mẫu nhị phân giữa iPhone X và iPhone 11

Tệp vá kernel dạng khai báo

  • Pongo cho phép dùng các bản vá checkra1n hiện có cho nhiều phiên bản iOS, nhưng cách áp dụng động khó đọc, khó sửa và khó chia sẻ
  • Để xử lý như các bản vá mã thực sự, họ tạo tệp bản vá dạng khai báo bằng công cụ nội bộ
    • Diff hai Mach-O để tạo tệp vá dạng văn bản dựa trên khác biệt assembly
    • Viết một chương trình riêng để áp dụng tệp vá đã tạo vào binary
  • Sau khi boot bằng Pongo, họ dùng QEMU monitor để dump các vùng bộ nhớ mà Pongo đã vá
  • Sau đó, họ tái dựng kernel đã vá và tạo một tệp vá lớn chứa toàn bộ thay đổi
  • Bằng cách chia nhỏ tệp vá lớn và thêm chú thích, họ có thể rà soát và kiểm soát phần nào của kernel được vá

Chiến lược vẽ màn hình khi không có GPU

  • Việc render đồ họa trên iPhone đời mới cuối cùng đều đi qua API Metal của Apple và cần GPU thật
  • Họ cho rằng mô phỏng GPU Apple Silicon là quá phức tạp, nên xem xét hai lựa chọn
    • Render bằng phần mềm dùng bootarg gpu=0 như từng khả thi trên các bản iOS cũ
    • Chuyển tiếp các lời gọi Metal sang iPhone thật hoặc máy Mac chạy macOS để thực hiện render
  • Trên iOS 14, tùy chọn bootarg gpu=0 của kernel XNU đã không còn
  • Phân tích framework QuartzCore bằng Ghidra cho thấy render phần mềm được gọi như cơ chế fallback khi không có renderer Metal
  • Trên iPhone jailbreak thật, họ vá QuartzCore và xác nhận việc sử dụng render phần mềm
    • UI chậm hơn nhiều
    • Một số vùng xuất hiện artifact, có thể vì các phần đó yêu cầu render trực tiếp bằng Metal
  • Thử nghiệm này cho thấy trong phạm vi không dùng trực tiếp Metal hay OpenGL, tức phần lớn ứng dụng UIKit, QEMU cũng có thể render bằng phần mềm

Thử nghiệm proxy lời gọi Metal

  • Họ cũng thử phương án proxy lời gọi Metal với hai iPhone vật lý
    • Parse toàn bộ header iOS bằng LLVM
    • Biểu diễn con trỏ đối tượng Objective-C trên server thành con trỏ stub ở client
    • Tự động tạo mã trao đổi struct và pointer
    • Hook toàn bộ hàm và method
    • Chuyển tiếp mọi lời gọi sang server và trả về kết quả thực thi
  • Một số vòng gọi cơ bản trong quá trình khởi tạo Metal đã thành công
  • Tuy nhiên, ngôn ngữ Objective-C và API Metal phức tạp, nhiều tính năng, nên khối lượng công việc để chạy thực sự là rất lớn
  • Họ tạm hoãn cách này và quyết định giải quyết các vấn đề khác trước bằng render phần mềm, dù có giới hạn
  • Các framework iOS cũng lộ ra private API không có trong header công khai; tuy có cách parse để tạo header, đa số rất khó dùng trực tiếp và làm tăng độ phức tạp

Debug IOSurface và framebuffer

  • Ngay cả sau khi thử render phần mềm, vẫn cần ít nhất một thiết bị framebuffer tối thiểu, nhưng QEMU t8030 gốc chưa triển khai phần này
  • Họ tìm thấy fork QEMUAppleSilicon có công việc hỗ trợ IOMFB và dùng nó để debug hiển thị
  • Khi khôi phục iOS bằng phiên bản này, logo Apple và thanh tiến trình xuất hiện, nhưng khi boot bình thường thì màn hình vẫn hoàn toàn đen
  • Sau khi xem kext IOMFB bằng Ghidra và kiểm tra triển khai framebuffer của QEMU, có vẻ tồn tại hai chế độ
    • Raw framebuffer tại một địa chỉ phần cứng cố định
    • API phức tạp hơn, thiết lập nhiều plane bằng register và ghi dữ liệu surface bằng DMA
  • Có thể hiển thị một ARGB surface tùy ý bằng raw framebuffer, nhưng trong quá trình boot hệ thống không ghi vào framebuffer đó
  • Ở chế độ display thứ hai, trace cho thấy kernel thiết lập graphical plane bằng register, nhưng sau đó không có đầu ra màn hình

Tắt ngẫu nhiên hóa địa chỉ và debug bằng GDB

  • Chỉ có SSH thì việc quan sát hệ thống đang chạy bị giới hạn, nên cần debug kernel và user space bằng GDB
  • Ngẫu nhiên hóa địa chỉ kernel được thiết lập trong phần khởi tạo board t8030, nên có thể tắt hoàn toàn
  • Userland có ngẫu nhiên hóa file thực thi và ngẫu nhiên hóa thư viện động bên trong dyld cache
    • File thực thi được tắt bằng cách vá hàm _load_machfile của kernel
    • Các thư viện trong dyld cache được ngẫu nhiên hóa địa chỉ một lần khi boot, rồi được nạp ở cùng địa chỉ cho mọi file thực thi
  • dyld cache tại /System/Library/Caches/com.apple.dyld/dyld_shared_cache_arm64e chứa toàn bộ thư viện framework dưới dạng một blob nhị phân lớn
  • Họ viết công cụ C để dlopen toàn bộ thư viện framework và liệt kê các image đã load cùng offset bằng các hàm _dyld*
  • Kết hợp cách này với quá trình đảo ngược địa chỉ GDB, họ có thể debug các thư viện trong dyld cache
  • Các đối tượng đặc biệt được quan tâm là kext IOMFB, backboardd, SpringBoard, QuartzCore
  • Sau đó, họ cũng tìm được cách tắt dyld cache bằng bản vá kernel, và dùng thư viện Rust object của dự án Gimli để tìm trực tiếp địa chỉ ảo của dyld cache trên host
  • Việc debug user space cần GDB server trong guest; ví dụ, họ dùng gói debugserver của Procursus

Log hệ thống và vượt qua lockdownd

  • Qua GDB, backboardd có vẻ khởi động bình thường, nhưng để hiểu tình hình thực tế cần log hệ thống
  • Trên iPhone thật, sau khi pairing USB với máy tính, có thể xem log hệ thống bằng idevicesyslog
  • Quá trình pairing bao gồm tạo cặp khóa, private key được lưu trên iPhone và lockdownd xác minh danh tính máy tính
  • Trong môi trường mô phỏng, tương tác USB khả dụng nhưng lockdownd không hoạt động đúng
  • Phân tích bằng Ghidra cho thấy lockdownd cố dùng keybag để lưu private key, việc này yêu cầu SEP không tồn tại
  • Họ inject shellcode thay thế một số hàm hiện có để đọc cặp public/private key đã tạo sẵn từ filesystem và nạp lại mỗi khi lockdownd cố lấy từ keybag
  • Bằng debug và vá bổ sung, họ mô phỏng việc người dùng đã tin cậy máy tính và iPhone đã được mở khóa
  • Cuối cùng, có thể pairing với iPhone được mô phỏng từ companion QEMU
  • Log cho thấy QuartzCore khởi tạo bình thường, phát hiện kích thước màn hình và sử dụng fallback render phần mềm
  • Mọi thứ trông như bình thường, nhưng màn hình vẫn không hiển thị
  • Một lỗi đơn lẻ liên quan đến pixel format được vượt qua bằng cách ép chỉ định RGBA, nhưng về sau cách này được gỡ bỏ

Vấn đề PAC và port sang QEMU 8

  • Khi cố sửa lỗi pixel format của backboardd, các vấn đề bổ sung do tính năng bảo mật của iOS lộ ra
  • Kiểm tra chữ ký tại thời điểm load và runtime đã được giải quyết bằng bản vá kernel, nhưng khi chạy backboardd đã chỉnh sửa, xảy ra lỗi Pointer Authentication
  • Pointer Authentication là tính năng được thêm trong ARMv8.3, và đây là vấn đề mới gặp trên board t8030 đang được mô phỏng, thay vì t8015 từng dùng trước đó
  • Ban đầu, họ nghĩ đến cách thay tất cả lệnh PAC bằng NOP hoặc lệnh tương đương không PAC
  • Sau đó, họ xác nhận binary ARM64 PAC có thể được build theo hai cách
    • Dùng tập lệnh PAC chuyên biệt chỉ chạy trên CPU ARMv8.3+
    • Dùng tập lệnh “chưa sử dụng”, được diễn giải là PAC trên ARMv8.3+ và là lệnh tương đương không PAC trên ARM cũ
  • Họ kiểm chứng hành vi này trên buildroot và hệ thống ARM64 Linux, đồng thời xác nhận binary cho t8030 dùng arm64e, tập lệnh tương thích ngược
  • Họ cho rằng chỉ cần tắt PAC enforcing trong QEMU thì mã sẽ chạy như mã không PAC, nhưng QEMU 7 không hoạt động như vậy, còn QEMU 8 có hành vi khác
  • Họ port codebase hiện tại sang QEMU 8.2.1
    • Các lệnh riêng của Apple genter, gexit
    • Mã xử lý GL exception levels
    • Việc port khó vì có nhiều chỉnh sửa vào generic code của QEMU
  • Sau nhiều lần XNU panic, debug kernel bằng GDB, debug chính QEMU và git bisect, họ boot lại được iOS trên QEMU 8
  • Nhờ đó có thể tắt PAC và sửa bất kỳ đoạn mã thực thi nào tại vị trí mong muốn

Truy tìm nguyên nhân màn hình đen

  • Vì log hệ thống cho thấy backboardd có vẻ hoạt động bình thường, họ truy sâu hơn lý do không hiển thị
  • Nếu ghi trực tiếp raw ARGB frame vào địa chỉ, màn hình thật sự thay đổi, và có thể vẽ lên nhiều graphical plane
  • Do đó bản thân triển khai display có vẻ bình thường, còn lại ba khả năng
    • backboardd không ghi gì cả
    • Ghi vào địa chỉ không đúng
    • Dữ liệu được ghi không hợp lệ
  • Họ dùng QEMU monitor để lấy các địa chỉ vật lý không liên tục, rồi dùng script dump bộ nhớ DMA vật lý và ghép thành một file
  • Dù diễn giải bằng ffplay như ARGB frame, họ không thu được kết quả có ý nghĩa
  • Tiếp theo, họ đặt breakpoint tại iosurface_lock để lấy địa chỉ surface được map vào bộ nhớ backboardd và kiểm tra
  • Đôi khi phát hiện hình dạng kỳ lạ trông giống logo Apple, nhưng có vẻ có vấn đề với cách ghi frame
  • Khi thực hiện cùng thao tác trên iPhone 10 thật, họ dễ dàng dump được raw ARGB frame đầy đủ của màn hình hiện tại
  • Trên iPhone 11, tức từ t8030 trở đi, surface có vẻ được truyền ở dạng nén mà GPU có thể xử lý
  • Vì điều này không xảy ra trên t8015 của iPhone X, họ sửa DTB của QEMU để truyền chip-id là 8015 thay vì 8030
  • Kết quả là sau khi boot, logo Apple được hiển thị trên màn hình

Thanh tiến trình và bản vá xác thực

  • Ngay cả sau khi logo Apple hiển thị, UI vẫn không tiến xa hơn, và log hệ thống in ra rất nhiều thông điệp từ nhiều daemon và thư viện
  • Họ tiến hành bằng cách đoán lỗi nào thực sự liên quan đến vấn đề UI rồi sửa từng lỗi một
  • Họ xác nhận vấn đề liên quan đến xác thực người dùng, nguồn lỗi là daemon mobileactivationd và framework SpringBoardFoundation
  • Sau khi vá chúng, một thanh tiến trình màu trắng tương tự như trong giai đoạn khôi phục được hiển thị
  • Thanh tiến trình trông như đang chạy, nhưng sau vài giờ có vẻ dừng ở 90%

Cải thiện vòng lặp vá dyld cache và user space

  • Nhờ tắt ngẫu nhiên hóa địa chỉ, việc vá user space và các framework trong dyld cache trở nên khả thi
  • Tương tự kernel, họ tạo tệp vá dạng văn bản cho từng binary/thư viện và áp dụng bằng công cụ nội bộ
  • dyld cache khoảng 2GB, nên vá trực tiếp hoặc lặp lại việc sao chép qua SSH là không thực tế
  • Vì làm việc trong môi trường Linux, họ cũng không thể sửa trực tiếp NVMe
  • Họ mở rộng công cụ diff/patch nội bộ để tìm offset của framework bên trong blob dyld cache
  • Thêm tùy chọn tạo các lệnh dd đơn giản có thể áp dụng ngay trên iPhone, cùng lệnh revert
  • Sau khi remount filesystem ở chế độ đọc/ghi, có thể áp dụng lệnh dd để lặp nhanh các chỉnh sửa dyld cache
  • Để thay đổi có hiệu lực, chỉ cần reboot iOS
  • Để cách này hoạt động, cần thêm một số bản vá cho kiểm tra chữ ký của kernel

Chạy PreBoard và hiển thị màn hình UIKit

  • Trước khi xử lý thanh tiến trình bị kẹt, họ thử nghiệm tiến trình hệ thống PreBoard
  • PreBoard dường như chỉ được hiển thị cho người dùng khi có vấn đề như gián đoạn cập nhật
  • Đây là ứng dụng hệ thống vẽ trực tiếp qua backboardd giống SpringBoard, nên có thể khởi chạy ngay từ dòng lệnh
  • Kết quả là một màn hình trắng yêu cầu “swipe to upgrade” được hiển thị
  • Dựa trên kinh nghiệm từng dùng VNC server trên iPhone vật lý, họ thêm VNC, và sau nhiều lần thất bại, mở khóa màn hình không phải bằng thao tác vuốt mà bằng phím bàn phím
  • Ngay sau khi mở khóa, QEMU dừng thực thi vì iOS dùng illegal instruction
  • Phân tích backboardd cho thấy framework vImage dùng lệnh AMX (Apple Matrix Coprocessor) cho các phép toán đồ họa tăng tốc phần cứng như _vHorizontal_Scale_ARGB_8888_Accelerate
  • AMX là tập lệnh riêng của Apple chưa được triển khai trong CPU ARM mô phỏng của QEMU
  • Framework vImage có phiên bản phần mềm thay thế chỉ dùng lệnh ARM generic, nên họ lại vá để dùng phiên bản này
  • Kết quả cuối cùng là một cửa sổ UIKit thật sự được hiển thị, cùng màn hình nhập mật mã và textbox hoạt động
  • Có thể nhập vào textbox thông qua sự kiện bàn phím được inject bằng VNC
  • Tại thời điểm này, các thành phần cần thiết để SpringBoard hiển thị đúng đã sẵn sàng, và việc khởi chạy chỉ còn là vấn đề thời gian
  • Bài tiếp theo tiếp tục ở Part 2

1 bình luận

 
GN⁺ 2025-04-07
Ý kiến trên Hacker News
  • Hy vọng https://github.com/devos50/qemu-ios sẽ phát triển để hỗ trợ đến iPhone OS 3.x, để có thể trải nghiệm các ứng dụng iPhone đời đầu ở góc độ bảo tồn số
    https://github.com/touchHLE/touchHLE cũng rất tuyệt, nhưng ngoài các ứng dụng cực kỳ cơ bản thì cần bản vá riêng cho từng ứng dụng

    • iPhone 11 được giả lập bằng QEMU có thể hỗ trợ từ iOS 13.x đến iOS 18.x: https://github.com/ChefKissInc/QEMUAppleSilicon
      Để chạy ứng dụng 32-bit trên iOS 10, QEMU cũng cần hỗ trợ iPhone 7
    • Sẽ thật tuyệt nếu có thể dùng lại các trò chơi đời đầu hiện không có lựa chọn thay thế, hoặc những ứng dụng cũ rất hay
  • Trước đây tôi từng giả lập NumWorks N0100[1] và HP Prime G1[2] bằng QEMU, đến mức firmware chính thức thực sự chạy được
    [1] https://github.com/boricj/qemu/tree/numworks_calculators
    [2] https://github.com/boricj/qemu/tree/s3c2416-boricj

  • Nếu ứng dụng dự án này theo hướng thú vị, có lẽ có thể cài một image pmOS tối giản cùng phiên bản QEMU này lên một điện thoại có hỗ trợ phần cứng postmarketOS khá tốt, rồi boot iOS trên điện thoại Android
    Cũng có thể tùy biến QEMU thêm để chuyển tiếp phần cứng điện thoại như modem, Bluetooth vào máy ảo iOS

    • Ý tưởng thú vị, nhưng vấn đề đầu tiên là tìm được một điện thoại mà trên postmarketOS vừa dùng được camera vừa gọi điện thoại ổn định
      Chỉ riêng việc đó mà làm được, chưa cần giả lập iOS/Android, thì cũng đã hài lòng rồi
    • Nếu chỉ làm cho vui thì ổn, nhưng trên thực tế sẽ cực kỳ kém hiệu quả nên khó trở thành một thiết bị dùng được, và khối lượng công việc cũng khổng lồ
    • Có cảm giác giống một kiểu bán ảo hóa nào đó? Có vẻ sẽ là một dự án thú vị
  • Phiên bản lưu trữ: https://archive.ph/l1CwO

  • Điều này có nghĩa là giờ có thể làm những việc như kiểm thử Safari hay biên dịch cho iOS trên hệ thống Linux mà không cần phần cứng Apple sao?

  • https://github.com/ChefKissInc/QEMUAppleSilicon
    Đây là thiết bị Apple Silicon được giả lập trong QEMU, hiện chỉ hỗ trợ iPhone 11
    Video demo: https://nitter.poast.org/eshard/status/1908162866609311962

  • Tôi đã thử làm theo hướng dẫn chạy: https://github.com/TrungNguyen1909/qemu-t8030/wiki/Bringing-...
    Nó crash nhiều lần đến mức tôi không muốn thừa nhận, nhưng quả là khá hay

  • Không thấy nhắc gì đến kết nối mạng. Có vẻ họ không giả lập Wi-Fi hay chipset modem di động
    Tôi tò mò không biết sẽ kết nối thiết bị giả lập này với Internet bằng cách nào. Có thể là kiểu Ethernet qua USB chẳng hạn

    • iOS hỗ trợ USB Ethernet
  • Cần điều gì để Apple chấp nhận phát triển iOS đa nền tảng?

    • Apple chắc chắn sẽ không bao giờ làm vậy. Những gì họ làm từ trước đến nay cho thấy điều ngược lại: kiểm soát hoàn toàn thiết bị và hệ sinh thái, không hợp tác với các công ty khác về tiêu chuẩn, và kiểm soát App Store chặt chẽ
      Từ góc nhìn của Apple, họ chẳng được gì khi cho phép mô hình phát triển như thế
    • Apple bán phần cứng bằng phần mềm. Đó là lý do iMessage cho Android chưa xuất hiện
      Để chuyện như vậy xảy ra, chính cách Apple nhìn thế giới phải thay đổi; có lẽ đó sẽ là một thay đổi lớn cỡ việc Microsoft phần nào chấp nhận Linux với WSL và .NET cho Linux
    • Văn hóa doanh nghiệp phải thay đổi hoàn toàn
    • Apple là công ty phần cứng. Có lý do gì để họ muốn hỗ trợ thứ gì đó trên phần cứng mà họ không bán?
    • Có lẽ phải đến mức bị đe dọa chia tách như thời Internet Explorer
  • repository nào có thể tái hiện việc này không?