QEMU ile iPhone emülasyonu yapmak
(eshard.com)- eShard, mevcut açık kaynak iOS emülasyonu çalışmalarını temel alarak iOS 14’ü QEMU’da boot etmeyi ve UI ile bazı uygulamaları çalıştırabilen bir emülatör hedefliyor
- QEMU’nun içine doğrudan kernel patch’leri eklemek yerine PongoOS ve checkra1n KPF kullanarak XNU patch’lerini ayırdı; Mach-O diff tabanlı araçlarla patch içeriklerinin incelenebilmesini sağladı
- Apple Silicon GPU emülasyonu çok geniş kapsamlı olduğu için önce yazılımsal rendering seçildi; jailbreak’li iPhone’da
QuartzCorepatch’iyle UIKit UI’ın yavaş da olsa çizilebildiği doğrulandı - Ekranın siyah kalması sorununu çözmek için adres randomizasyonunu devre dışı bırakma, GDB debugging,
lockdowndpairing bypass, PAC devre dışı bırakma, QEMU 8.2.1’e port etme ve dyld cache patch otomasyonuna devam edildi - Sonuçta QEMU ekranında UIKit passcode giriş UI’ı gösterildi ve VNC klavye girişiyle text box kontrol edildi;
SpringBoardgösterimi için gerekli temel bileşenler hazırlandı
iOS emülasyonunun başlangıç noktası
- Mevcut açık kaynak çözümleri incelerken alephsecurity/xnu-qemu-arm64 çalıştırılmıştı, ancak proje salt okunur durumdaydı
- Daha sonra başlangıç noktası olarak TrungNguyen1909/qemu-t8030 kullanıldı
- İkinci bir “companion” QEMU üzerinden USB bağlantısıyla iOS restore edilebiliyor
- iOS 14 çalıştırma desteği var
- Daha güncel bir QEMU sürümünü temel alıyor
- Emülatörü çalıştırma yöntemini anlatan bir wiki sunuyor
System/Library/xpc/launchd.plistdeğiştirilerek hızlıca shell ve SSH erişimi sağlandı- Uzun vadeli hedef, UI’a sahip ve en azından bazı uygulamaları çalıştırabilen işlevsel iOS emülasyonu
Kernel patch’lerini PongoOS ile ayırma
t8030projesi XNU kernel patch kodunu QEMU’nun kendisine eklemişti; ancak ileride patch sayısının artma olasılığı nedeniyle daha temiz bir yapıya ihtiyaç vardı- Gerçek jailbreak’li iPhone deneyimine dayanarak PongoOS ile checkra1n patch’lerini uygulama yöntemi incelendi
- Normal jailbreak akışında, cihaz checkmate ile pwn edildikten sonra PongoOS SRAM’e enjekte edilir ve
checkra1n-kpfmodülü USB üzerinden aktarılır- Bu çalışmada ilk USB işlemlerinden kaçınmak için emüle edilen iPhone’un SRAM’i genişletildi ve PongoOS ile checkra1n KPF modülü kullanıldı
- PongoOS’un ilk çalıştırılmasında bootrom veya iBoot’un yaptığı başlatma kodu olmadığı için sorunlar çıktı
- double/float komutlarından önce FPU ayarı gerekiyordu
- ARM dokümantasyonu ve mevcut QEMU ile ilgili kodlar referans alınarak çözüldü
- A13 sonrası cihaz özelliklerini Pongo desteklemediği için bazı patch’lerin pattern matching’i bozuldu
- Pointer Authentication(PAC) komutları olan
autda,xpacdeklendi - Apple farklı bir slide kullanıyor
task_for_pid(tfp0)patch’inde iPhone X ile iPhone 11’in adres ve binary pattern farkları doğrulandı
- Pointer Authentication(PAC) komutları olan
Bildirimsel kernel patch dosyası
- Pongo, çeşitli iOS sürümleri için mevcut checkra1n patch’lerini kullanmayı sağlıyordu; ancak dinamik uygulama yöntemi okumayı, değiştirmeyi ve paylaşmayı zorlaştırıyordu
- Bunları gerçek kod patch’i gibi ele almak için dahili bir araçla bildirimsel patch dosyaları üretildi
- İki
Mach-Odiff edilerek assembly farklarına dayalı metin patch dosyası oluşturuldu - Oluşturulan patch dosyasını binary’ye uygulayan ayrı bir program yazıldı
- İki
- Pongo ile boot ettikten sonra QEMU monitor kullanılarak Pongo’nun patch’lediği bellek bölümleri dump edildi
- Ardından patch’lenmiş kernel yeniden oluşturuldu ve tüm değişiklikleri içeren büyük bir patch dosyası üretildi
- Büyük patch parçalara ayrılıp yorumlanarak kernel’in hangi bölümlerinin patch’lendiği incelenebilir ve kontrol edilebilir hale geldi
GPU olmadan ekran çizme stratejisi
- Yeni iPhone’lardaki grafik rendering sonunda Apple’ın Metal API’sinden geçiyor ve gerçek GPU gerektiriyor
- Apple Silicon GPU’yu emüle etmenin fazla karmaşık olacağı düşünülerek iki seçenek incelendi
- Eski iOS’larda mümkün olduğu gibi
gpu=0bootarg ile yazılımsal rendering - Metal çağrılarını gerçek bir iPhone’a veya macOS çalıştıran bir Mac’e ileterek rendering yaptırmak
- Eski iOS’larda mümkün olduğu gibi
- iOS 14’te XNU kernel’in
gpu=0bootarg seçeneği kaldırılmıştı QuartzCoreframework’ü Ghidra ile analiz edildiğinde, yazılımsal rendering’inMetalrenderer olmadığında çağrılan bir fallback yapısı olduğu görüldü- Gerçek jailbreak’li iPhone’da
QuartzCorepatch’lenerek yazılımsal rendering kullanımı doğrulandı- UI çok daha yavaştı
- Bazı alanlarda artefact’ler oluştu; bu bölümler doğrudan
Metalrendering gerektiriyor olabilir
- Bu deneyimle,
MetalveyaOpenGLi doğrudan kullanmayan kapsamda, yani çoğu UIKit uygulamasında, QEMU’da da yazılımsal rendering’in mümkün olabileceği düşünüldü
Metal çağrılarını proxy etme deneyi
- Metal çağrılarını proxy etme alternatifi de iki fiziksel iPhone ile denendi
- LLVM ile tüm iOS header’larının parse edilmesi
- Sunucudaki Objective-C nesne pointer’larının istemcide stub pointer olarak temsil edilmesi
- Struct ve pointer değişim kodunun otomatik üretilmesi
- Tüm function ve method’ların hook edilmesi
- Tüm çağrıların sunucuya iletilip yürütme sonucunun döndürülmesi
- Metal initialization için temel çağrı gidiş dönüşleri kısmen başarılı oldu
- Ancak Objective-C dili ve
MetalAPI’si karmaşık ve çok kapsamlı olduğu için gerçek çalışmaya ulaşmak için gereken iş yükü çok büyüktü - Bu yöntem sonraya bırakıldı; sınırlamaları olsa da önce diğer sorunları yazılımsal rendering ile çözmeye karar verildi
- iOS framework’lerinde public header’larda olmayan private API’ler de açığa çıkıyordu; bunları parse edip header oluşturmanın yolu vardı, ancak çoğunu doğrudan kullanmak zordu ve karmaşıklığı artırıyordu
IOSurface ve framebuffer debugging
- Yazılımsal rendering denendikten sonra bile en azından temel bir framebuffer cihazı gerekiyordu; ancak orijinal
t8030QEMU’da bu uygulanmamıştı - IOMFB desteği üzerinde çalışılmış QEMUAppleSilicon fork’u bulunup display debugging için kullanıldı
- Bu sürümle iOS restore edilirken Apple logosu ve progress bar görünüyordu, ancak normal boot sırasında ekran tamamen siyah kalıyordu
- Ghidra ile IOMFB kext incelenip QEMU framebuffer implementasyonuna bakıldığında iki mod olduğu görüldü
- Sabit donanım adresindeki raw framebuffer
- Register’larla birden fazla plane ayarlayıp DMA ile surface verisi yazan daha karmaşık bir API
- Raw framebuffer ile rastgele bir ARGB surface gösterilebiliyordu, ancak boot sırasında sistem bu framebuffer’a yazmıyordu
- İkinci display modunda kernel’in register’larla graphical plane ayarladığı trace görülüyordu; ancak sonrasında ekrana çıktı gelmiyordu
Adres randomizasyonunu kapatma ve GDB debugging
- Yalnızca SSH erişimiyle çalışan sistemi gözlemlemenin sınırları vardı; kernel ve user space’i GDB ile debug etme ihtiyacı doğdu
- Kernel adres randomizasyonu
t8030board initialization’da ayarlanıyordu, bu yüzden tamamen kapatılabildi - Userland tarafında executable randomization ve dyld cache içindeki dynamic library randomization vardı
- Executable için kernel’in
_load_machfilefonksiyonu patch’lenerek devre dışı bırakıldı - dyld cache library’leri boot sırasında bir kez adres randomizasyonuna uğruyor ve sonra tüm executable’larda aynı adrese load ediliyordu
- Executable için kernel’in
/System/Library/Caches/com.apple.dyld/dyld_shared_cache_arm64eiçindeki dyld cache, tüm framework library’lerini büyük bir binary blob olarak içeriyor- Tüm framework library’lerini
dlopeneden ve_dyld*fonksiyonlarıyla load edilmiş image’ları ve offset’leri listeleyen bir C aracı yazıldı - Bu yöntem ile GDB adreslerini geri çevirme süreci birlikte kullanılarak dyld cache’teki library’ler debug edilebildi
- Özellikle ilgi odağı
IOMFBkext,backboardd,SpringBoardveQuartzCoreidi - Daha sonra kernel patch’iyle dyld cache’i devre dışı bırakmanın yolu da bulundu; host üzerinde dyld cache’in virtual address’ini doğrudan bulmak için Gimli projesinin Rust object library’si kullanıldı
- User space debugging için guest tarafında GDB server gerekiyordu; örnek olarak Procursus
debugserverpaketi kullanıldı
Sistem log’ları ve lockdownd bypass
- GDB ile
backboarddnormal şekilde başlıyor gibi görünüyordu, ancak gerçek durumu anlamak için sistem log’ları gerekiyordu - Gerçek iPhone’da bilgisayarla USB pairing sonrasında
idevicesyslogile sistem log’ları görülebilir - Pairing süreci anahtar çifti üretimini içerir; private key iPhone’da saklanır ve
lockdowndbilgisayarın kimliğini doğrular - Emülasyon ortamında USB etkileşimi mümkündü, ancak
lockdownddüzgün çalışmıyordu - Ghidra analizi sonucunda
lockdowndun private key saklamak içinkeybagkullanmaya çalıştığı ve bunun mevcut olmayan SEP gerektirdiği anlaşıldı - Mevcut bazı fonksiyonların yerine geçen shellcode enjekte edilerek önceden oluşturulmuş public/private key çifti dosya sisteminden okunacak ve
lockdowndherkeybagden almaya çalıştığında yüklenecek şekilde düzenlendi - Ek debugging ve patch’lerle kullanıcının bilgisayara güvendiği ve iPhone’un kilidinin açık olduğu simüle edildi
- Sonunda companion QEMU’dan emüle edilen iPhone ile pairing yapılabildi
- Log’larda
QuartzCoreun normal başlatıldığı, display size’ı algıladığı ve yazılımsal rendering fallback’ini kullandığı doğrulandı - Her şey normal görünmesine rağmen ekran hâlâ görüntülenmiyordu
- Pixel format ile ilgili tek bir hata RGBA’yı zorlayarak bypass edildi, ancak daha sonra kaldırıldı
PAC sorunu ve QEMU 8’e port etme
backboardddaki pixel format hatası düzeltilmeye çalışılırken iOS güvenlik özellikleri nedeniyle ek bir sorun ortaya çıktı- Load zamanındaki ve runtime’daki signature kontrolleri kernel patch’iyle çözülmüştü, ancak değiştirilmiş
backboarddçalışırken Pointer Authentication hatası oluştu - Pointer Authentication, ARMv8.3’e eklenen bir özellik; daha önce kullanılan t8015’te değil, emüle edilen
t8030board’da yeni karşılaşılan bir sorundu - İlk başta tüm PAC komutlarını NOP’a veya PAC’siz eşdeğer komutlara dönüştürme yöntemi düşünüldü
- Sonra ARM64 PAC binary’lerinin iki şekilde build edilebildiği doğrulandı
- Yalnızca ARMv8.3+ CPU’larda çalışan özel PAC instruction set kullanımı
- ARMv8.3+’ta PAC olarak yorumlanan, eski ARM’de ise PAC’siz eşdeğer komut olarak yorumlanan “kullanılmayan” instruction set kullanımı
- buildroot ve ARM64 Linux sisteminde test edilerek bu davranış doğrulandı;
t8030için binary’lerin backward compatible instruction set olan arm64e kullandığı belirlendi - QEMU’da yalnızca PAC enforcing kapatılırsa kodun PAC’siz kod gibi çalışacağı düşünüldü; ancak QEMU 7’de çalışmadı, QEMU 8’de davranış farklıydı
- Mevcut codebase QEMU 8.2.1’e port edildi
- Apple’a özel
genter,gexitkomutları - GL exception levels işleme kodu
- QEMU generic code üzerinde çok sayıda değişiklik olduğu için port zordu
- Apple’a özel
- Birçok XNU panic, kernel GDB debugging, QEMU’nun kendi debugging’i ve git bisect sonunda iOS yeniden QEMU 8’de boot ettirildi
- Böylece PAC devre dışı bırakılabildi ve istenen herhangi bir executable code istenen konumda değiştirilebilir hale geldi
Siyah ekranın nedenini izleme
- Sistem log’larında
backboarddnormal çalışıyor gibi göründüğü için görüntü gelmemesinin nedeni daha derinlemesine izlendi - Raw ARGB frame doğrudan adrese yazılınca gerçek display değişiyor ve birden fazla graphical plane’e çizim yapılabiliyordu
- Dolayısıyla display implementasyonu sağlam görünüyordu; geriye üç olasılık kalıyordu
backboarddhiçbir şey yazmıyor- Yanlış adrese yazıyor
- Yazılan veri geçerli değil
- QEMU monitor ile non-contiguous physical address’ler alındı; script ile physical DMA memory dump edilip tek bir dosyada birleştirildi
ffplayile ARGB frame gibi yorumlandı, ancak anlamlı bir sonuç alınamadı- Sonra
iosurface_locküzerine breakpoint konarakbackboarddbelleğine map edilmiş surface adresleri alındı ve incelendi - Zaman zaman Apple logosuna benzeyen garip şekiller bulundu, ancak frame yazma biçiminde sorun var gibi görünüyordu
- Aynı işlem gerçek iPhone 10’da yapılınca mevcut ekranın tam raw ARGB frame’i kolayca dump edilebildi
- iPhone 11, yani
t8030sonrası cihazlarda surface’in GPU’nun işleyebileceği sıkıştırılmış biçimde iletildiği düşünüldü - iPhone X’in
t8015inde bu gerçekleşmediği için QEMU’nun DTB’sindechip-id8030 yerine 8015 olarak iletilecek şekilde değiştirildi - Bunun sonucunda boot sonrası ekranda Apple logosu göründü
Progress bar ve aktivasyon patch’i
- Apple logosu göründükten sonra bile UI daha ileri gitmedi; sistem log’larında birçok daemon ve library’den çok sayıda mesaj çıkıyordu
- Hangi hatanın gerçek UI sorunuyla ilgili olduğunu tahmin edip tek tek düzeltme yöntemiyle ilerlenildi
- Kullanıcı aktivasyonu ile ilgili bir sorun tespit edildi; hatanın kaynağı
mobileactivationddaemon’u veSpringBoardFoundationframework’üydü - Bunlar patch’lendikten sonra restore aşamasında görülenlere benzer beyaz progress bar gösterildi
- Progress bar hareket ediyor gibi görünüyordu; ancak saatler sonra bile %90’da kalmış gibi duruyordu
dyld cache ve user space patch döngüsünü iyileştirme
- Adres randomizasyonunun devre dışı bırakılması sayesinde user space ve dyld cache framework patch’leri mümkün oldu
- Kernel ile aynı yöntemle her binary/library için metin patch dosyaları oluşturulup dahili araçla uygulandı
- dyld cache yaklaşık 2GB olduğu için doğrudan patch’lemek veya SSH üzerinden tekrar tekrar kopyalamak pratik değildi
- Linux ortamında çalışıldığı için NVMe doğrudan değiştirilemiyordu
- Dahili diff/patch aracı dyld cache’e uyarlanarak framework offset’lerini dyld cache blob içinde bulacak şekilde genişletildi
- iPhone’da doğrudan uygulanabilecek basit
ddkomutları ve revert komutları üretme seçeneği eklendi - Dosya sistemi read/write modunda yeniden mount edildikten sonra
ddkomutları uygulanınca dyld cache değişiklikleri hızlıca tekrarlanabildi - Değişikliklerin etkili olması için yalnızca iOS reboot gerekiyordu
- Bu yöntemin çalışması için kernel’in signature check’lerine yönelik bazı ek patch’ler gerekiyordu
PreBoard çalıştırma ve UIKit ekranını gösterme
- Takılan progress bar çözülmeden önce sistem process’i
PreBoardile deneme yapıldı PreBoardun yalnızca update kesintisi gibi sorunlarda kullanıcıya gösterildiği düşünülüyorduSpringBoardgibibackboarddüzerinden doğrudan çizen bir sistem uygulaması olduğundan command line’dan doğrudan başlatılabiliyordu- Çalıştırılınca “swipe to upgrade” isteyen beyaz bir ekran gösterildi
- Geçmişte fiziksel iPhone’da VNC server kullanma deneyimine dayanarak VNC eklendi; birçok başarısız denemeden sonra ekran swipe ile değil klavye tuşlarıyla unlock edildi
- Unlock işleminden hemen sonra QEMU, iOS’un illegal instruction kullanması nedeniyle yürütmeyi durdurdu
backboarddanalizi sonucundavImageframework’ünün_vHorizontal_Scale_ARGB_8888_Accelerategibi donanım hızlandırmalı grafik işlemleri için AMX(Apple Matrix Coprocessor) komutları kullandığı anlaşıldı- AMX, QEMU’nun emülasyon ARM CPU’sunda uygulanmamış Apple’a özgü bir instruction set’tir
vImageframework’ünde yalnızca generic ARM komutları kullanan alternatif yazılımsal sürümler vardı; bunların kullanılması yine patch ile sağlandı- Nihai sonuç olarak gerçek
UIKitpenceresi gösterildi; passcode giriş ekranı ve çalışan bir text box ortaya çıktı - VNC ile enjekte edilen klavye event’leri üzerinden text box’a giriş yapılabildi
- Bu noktada
SpringBoardun düzgün gösterilmesi için gereken bileşenler hazırdı; başlaması yalnızca zaman meselesi gibi görünüyordu - Sonraki yazı Part 2 ile devam ediyor
1 yorum
Hacker News yorumları
https://github.com/devos50/qemu-ios projesinin iPhone OS 3.x'e kadar destek verecek şekilde gelişip, ilk dönem iPhone uygulamalarını dijital koruma açısından deneyimleyebilmemiz güzel olurdu.
https://github.com/touchHLE/touchHLE de harika, ancak çok temel uygulamalar dışında uygulama bazında yama gerektiriyor.
iOS 10'da 32 bit uygulamaları çalıştırmak için QEMU'nun iPhone 7'yi de desteklemesi gerekiyor.
Geçmişte QEMU ile NumWorks N0100[1] ve HP Prime G1[2]'i emüle edip, resmi firmware'in gerçekten çalıştığı seviyeye kadar getirmiştim.
[1] https://github.com/boricj/qemu/tree/numworks_calculators
[2] https://github.com/boricj/qemu/tree/s3c2416-boricj
Bu projeye eğlenceli bir uygulama olarak, postmarketOS donanım desteği oldukça iyi olan bir telefona minimum yapılandırmalı bir pmOS imajı ve QEMU'nun bu sürümü kurulup, bir Android telefonda iOS boot edilebilir gibi görünüyor.
QEMU'yu daha da özelleştirerek modem, Bluetooth gibi telefon donanımlarını iOS sanal makinesine aktarmak da mümkün olabilir.
iOS/Android emülasyonu olmadan sadece bu bile olsa memnun olurdum.
Arşivlenmiş sürüm: https://archive.ph/l1CwO
Bu artık Apple donanımı olmadan Linux sisteminde Safari testi veya iOS için derleme gibi şeyler yapabileceğimiz anlamına mı geliyor?
https://github.com/ChefKissInc/QEMUAppleSilicon
QEMU'da emüle edilen bir Apple Silicon cihazı; şu anda yalnızca iPhone 11 destekleniyor.
Demo videosu: https://nitter.poast.org/eshard/status/1908162866609311962
Çalıştırma yönergelerini izledim: https://github.com/TrungNguyen1909/qemu-t8030/wiki/Bringing-...
Kabul etmek istemeyeceğim kadar çok kez çöktü ama yine de epey havalı.
Ağ bağlantısından bahsedilmiyor. Wi‑Fi veya hücresel modem yonga setlerini emüle etmiyor gibi görünüyor.
Bu emüle edilen cihazın internete nasıl bağlanacağını merak ediyorum. USB üzerinden Ethernet gibi bir yöntem olabilir.
Apple'ın çok platformlu iOS geliştirmeyi kabul etmesi için ne gerekir?
Apple açısından bakınca böyle bir geliştirme modeline izin vererek kazanacağı bir şey yok.
Böyle bir şeyin gerçekleşmesi için Apple'ın dünyaya bakış biçiminin değişmesi gerekir; bu da Microsoft'un WSL ve Linux için .NET ile Linux'u bir ölçüde benimsemesi kadar büyük bir değişim olurdu.
Bunu yeniden üretmeyi sağlayan bir depo var mı?