
Output dari log kernel setelah menyusun dan menjalankan example/open1_hook.c
Xnuspy adalah modul Pongoos yang menginstal panggilan sistem baru, xnuspy_ctl , yang memungkinkan Anda untuk mengaitkan fungsi kernel dari ruang pengguna. Ini mendukung iOS 13.x, iOS 14.x, dan iOS 15.x pada checkra1n 0.12.2 dan lebih tinggi. Perangkat 4K tidak didukung.
Modul ini benar -benar mensterilkan KTRR/KPP dan memungkinkan untuk membuat memori RWX di dalam EL1. Jangan gunakan ini pada pengemudi harian Anda.
Membutuhkan libusb : brew install libusb
Jalankan make di Direktori Top Level. Ini akan membangun loader dan modul.
Tambahkan ini sebelum make .
XNUSPY_DEBUG=1kprintf ).XNUSPY_SERIAL=1IOLog .XNUSPY_LEAKED_PAGE_LIMIT=n64 . Info lebih lanjut dapat ditemukan di bawah debugging kernel panik.XNUSPY_TRAMP_PAGES=n XNUSPY_DEBUG dan XNUSPY_SERIAL tidak saling bergantung.
Setelah Anda membangun semuanya, minta checkra1n boot perangkat Anda ke pongo shell: /Applications/checkra1n.app/Contents/MacOS/checkra1n -p
Di direktori yang sama Anda membangun loader dan modul, lakukan loader/loader module/xnuspy . Setelah melakukan itu, Xnuspy akan melakukan pekerjaannya dan dalam beberapa detik perangkat Anda akan boot. loader akan menunggu beberapa detik lagi setelah mengeluarkan xnuspy-getkernelv jika seprom perlu dieksploitasi.
Terkadang beberapa ponsel saya terjebak di "booting" setelah KPF Checkra1n berjalan. Saya belum mencari tahu apa yang menyebabkan ini, tetapi jika itu terjadi, coba lagi. Juga, jika perangkat menggantung setelah bootx , coba lagi. Akhirnya, menandai kode xnuspy_ctl yang dikompilasi sebagai dieksekusi pada iPhone X saya yang menjalankan iOS 13.3.1 agak buruk, tetapi berhasil 100% dari waktu saya di ponsel saya yang lain. Jika Anda panik dengan instruksi kernel, ambil dibatalkan saat Anda menjalankan program kait Anda, coba lagi.
xnuspy akan menambal panggilan sistem enosys untuk menunjuk ke xnuspy_ctl_tramp . Ini adalah trampolin kecil yang menandai kode xnuspy_ctl yang dikompilasi sebagai dapat dieksekusi dan bercabang. Anda dapat menemukan implementasi xnuspy_ctl di module/el1/xnuspy_ctl/xnuspy_ctl.c dan contoh dalam direktori example .
Di dalam include/xnuspy/ is xnuspy_ctl.h , header yang mendefinisikan konstanta untuk xnuspy_ctl . Ini dimaksudkan untuk dimasukkan dalam semua program yang menghubungkan fungsi kernel.
Anda dapat menggunakan sysctlbyname untuk mengetahui panggilan sistem mana yang ditambal:
size_t oldlen = sizeof(long);
long SYS_xnuspy_ctl = 0;
sysctlbyname("kern.xnuspy_ctl_callnum", &SYS_xnuspy_ctl, &oldlen, NULL, 0);
Panggilan sistem ini membutuhkan empat argumen, flavor , arg1 , arg2 , dan arg3 . Rasanya bisa XNUSPY_CHECK_IF_PATCHED , XNUSPY_INSTALL_HOOK , XNUSPY_REGISTER_DEATH_CALLBACK , XNUSPY_CALL_HOOKME , XNUSPY_CACHE_READ , XNUSPY_KREAD , XNUSPY_KWRITE , atau XNUSPY_GET_CURRENT_THREAD . Arti dari tiga argumen berikutnya tergantung pada rasanya.
XNUSPY_CHECK_IF_PATCHED Ini ada sehingga Anda dapat memeriksa apakah ada xnuspy_ctl . Memanggilnya dengan rasa ini akan menyebabkannya kembali 999 . Nilai -nilai argumen lain diabaikan.
XNUSPY_INSTALL_HOOK Saya merancang rasa ini agar sesuai dengan API MSHookFunction . arg1 adalah alamat fungsi kernel yang tidak ingin Anda kaitkan. Jika Anda menyediakan alamat slid, kemungkinan besar Anda akan panik. arg2 adalah penunjuk untuk fungsi penggantian yang kompatibel dengan Abi. arg3 adalah pointer untuk xnuspy_ctl untuk copyout alamat trampolin yang mewakili fungsi kernel asli. Ini bisa menjadi NULL jika Anda tidak berniat memanggil yang asli.
XNUSPY_REGISTER_DEATH_CALLBACKRasa ini memungkinkan Anda untuk mendaftarkan "callback kematian" opsional, fungsi Xnuspy akan menelepon saat program kait Anda keluar. Ini memberi Anda kesempatan untuk membersihkan apa pun yang Anda buat dari kait kernel Anda. Jika Anda membuat utas kernel, Anda akan memberitahu mereka untuk mengakhiri fungsi ini.
Panggilan balik Anda tidak dipanggil secara tidak sinkron, jadi jika Anda memblokir, Anda mencegah utas pengumpulan sampah Xnuspy untuk dieksekusi.
arg1 adalah pointer untuk fungsi panggilan balik Anda. Nilai -nilai argumen lain diabaikan.
XNUSPY_CALL_HOOKME hookme adalah rintisan rakitan kecil yang diekspor xnuspy melalui cache xnuspy untuk Anda kaitkan. Memanggil xnuspy_ctl dengan rasa ini akan menyebabkan hookme dipanggil, memberikan cara bagi Anda untuk dengan mudah mendapatkan eksekusi kode kernel tanpa harus mengaitkan fungsi kernel yang sebenarnya.
arg1 adalah argumen yang akan diteruskan ke hookme ketika dipanggil. Ini bisa NULL .
XNUSPY_CACHE_READ Rasa ini memberi Anda cara untuk membaca dari cache xnuspy. Ini berisi banyak hal yang berguna seperti kprintf , current_proc , kernel_thread_start , beberapa fungsi libc, dan slide kernel sehingga Anda tidak harus menemukannya sendiri. Untuk daftar lengkap ID cache, lihat example/xnuspy_ctl.h .
arg1 adalah salah satu ID cache yang ditentukan dalam xnuspy_ctl.h dan arg2 adalah pointer untuk xnuspy_ctl untuk copyout alamat atau nilai dari apa yang Anda minta. Nilai -nilai argumen lain diabaikan.
XNUSPY_KREADRasa ini memberi Anda cara mudah untuk membaca memori kernel dari ruang pengguna tanpa TFP0.
arg1 adalah alamat virtual kernel, arg2 adalah alamat buffer ruang pengguna, dan arg3 adalah ukuran buffer ruang pengguna. arg3 byte akan ditulis dari arg1 ke arg2 .
XNUSPY_KWRITERasa ini memberi Anda cara mudah untuk menulis ke memori kernel dari ruang pengguna tanpa TFP0.
arg1 adalah alamat virtual kernel, arg2 adalah alamat buffer ruang pengguna, dan arg3 adalah ukuran buffer ruang pengguna. arg3 byte akan ditulis dari arg2 ke arg1 .
XNUSPY_GET_CURRENT_THREADRasa ini memberikan ruang pengguna alamat kernel dari utas panggilan.
arg1 adalah pointer untuk xnuspy_ctl untuk copyout nilai pengembalian current_thread . Nilai -nilai argumen lain diabaikan.
Untuk semua rasa kecuali XNUSPY_CHECK_IF_PATCHED , 0 dikembalikan pada kesuksesan. Setelah kesalahan, -1 dikembalikan dan errno diatur. XNUSPY_CHECK_IF_PATCHED tidak mengembalikan kesalahan apa pun. XNU's mach_to_bsd_errno digunakan untuk mengonversi kern_return_t ke errno yang sesuai.
XNUSPY_INSTALL_HOOK errno diatur ke ...
EEXIST jika:arg1 .ENOMEM If:unified_kalloc mengembalikan NULL .ENOSPC jika:xnuspy_tramp gratis, struktur data internal ke xnuspy. Ini seharusnya tidak terjadi kecuali Anda mengaitkan ratusan fungsi kernel secara bersamaan . Jika Anda membutuhkan lebih banyak fungsi kait, periksa batas.ENOTSUP jika:ENOENT jika:mh_for_addr tidak dapat menentukan header Mach-O yang sesuai dengan arg2 di dalam ruang alamat penelepon.EFAULT If:EIO jika:mach_make_memory_entry_64 tidak mengembalikan entri memori untuk keseluruhan segmen __TEXT mach-o mach-o yang ditentukan dan __DATA . errno juga tergantung pada nilai pengembalian vm_map_wire_external , mach_vm_map_external , mach_make_memory_entry_64 , copyin , copyout , dan jika berlaku, fungsi inisialisasi satu kali.
Jika rasa ini mengembalikan kesalahan, fungsi kernel target tidak terpikat. Jika Anda melewati penunjuk non- NULL untuk arg3 , itu mungkin atau mungkin belum diinisialisasi. Tidak aman untuk digunakan jika itu.
XNUSPY_REGISTER_DEATH_CALLBACK errno diatur ke ...
ENOENT jika:Jika rasa ini mengembalikan kesalahan, panggilan balik Anda tidak terdaftar.
XNUSPY_CALL_HOOKME errno diatur ke ...
ENOTSUP jika:hookme terlalu jauh dari memori yang berisi struktur xnuspy_tramp . Ini ditentukan di dalam pongoos, dan hanya dapat terjadi jika xnuspy harus mundur ke kode yang tidak digunakan yang sudah ada di dalam kernelcache. Dalam hal ini, memanggil hookme hampir pasti akan menyebabkan kepanikan kernel, dan Anda harus mencari tahu fungsi kernel lain untuk dihubungkan. Jika rasa ini mengembalikan kesalahan, hookme tidak dipanggil.
XNUSPY_CACHE_READ errno diatur ke ...
EINVAL If:arg1 tidak mewakili apa pun dalam cache.arg1 adalah IO_LOCK , tetapi kernelnya adalah iOS 14.4.2 atau di bawah atau iOS 15.x.arg1 adalah IPC_OBJECT_LOCK , tetapi kernelnya adalah iOS 15.x.arg1 adalah IPC_PORT_RELEASE_SEND , tetapi kernelnya adalah iOS 14.5 atau lebih.arg1 adalah IPC_PORT_RELEASE_SEND_AND_UNLOCK , tetapi kernelnya adalah iOS 14.4.2 atau di bawah.arg1 adalah KALLOC_CANBLOCK , tetapi kernelnya adalah iOS 14.x atau lebih tinggi.arg1 adalah KALLOC_EXTERNAL , tetapi kernelnya adalah iOS 13.X.arg1 adalah KFREE_ADDR , tetapi kernelnya adalah iOS 14.x atau di atas.arg1 adalah KFREE_EXT , tetapi kernelnya adalah iOS 13.x.arg1 adalah PROC_REF , tetapi kernelnya adalah iOS 14.8 atau di bawah.arg1 adalah PROC_REF_LOCKED , tetapi kernelnya adalah iOS 15.x.arg1 adalah PROC_RELE , tetapi kernelnya adalah iOS 14.8 atau di bawah.arg1 adalah PROC_RELE_LOCKED , tetapi kernelnya adalah iOS 15.x.arg1 adalah VM_MAP_UNWIRE , tetapi kernelnya adalah iOS 15.x.arg1 adalah VM_MAP_UNWIRE_NESTED , tetapi kernelnya adalah iOS 14.8 atau di bawah. errno juga tergantung pada nilai pengembalian copyout dan jika berlaku, nilai pengembalian fungsi inisialisasi satu kali.
Jika rasa ini mengembalikan kesalahan, penunjuk yang Anda lewati untuk arg2 tidak diinisialisasi.
XNUSPY_KREAD dan XNUSPY_KWRITE errno diatur ke ...
EFAULT If:arg1 atau arg2 . Jika Anda dikompilasi dengan XNUSPY_DEBUG=1 , pesan tentang itu dicetak ke log kernel.Jika rasa ini mengembalikan kesalahan, memori kernel tidak dibaca/ditulis.
XNUSPY_GET_CURRENT_THREAD Jika copyout gagal, errno diatur ke nilai pengembaliannya.
Saat menulis fungsi penggantian, mudah untuk melupakan bahwa saya sedang menulis kode kernel. Inilah beberapa hal yang perlu diingat saat Anda menulis kait:
__TEXT program Anda . Anda akan panik jika, misalnya, Anda secara tidak sengaja memanggil printf , bukan kprintf . Anda perlu mengimplementasikan kembali fungsi LIBC apa pun yang ingin Anda hubungi jika fungsi itu belum tersedia melalui XNUSPY_CACHE_READ . Anda dapat membuat pointer fungsi ke fungsi kernel lain dan memanggilnya.PAGE_SIZE memperluas ke vm_page_size , bukan konstan. Anda perlu menonaktifkan PAN (pada A10+, yang juga tidak saya rekomendasikan) sebelum membaca variabel ini atau Anda akan panik.-fno-stack-protector dan -D_FORTIFY_SOURCE=0 dalam beberapa kasus, perangkat harus membaca ___stack_chk_guard dengan dereferencing Pointer Pengguna -Userspace lain, yang akan panik pada A10+.Skimming https://developer.apple.com/library/archive/documentation/darwin/conceptual/kernelprogramming/style/style.html juga direkomendasikan.
Bug tidak bisa dihindari saat menulis kode, jadi pada akhirnya Anda akan menyebabkan kepanikan kernel. Panik tidak berarti ada bug dengan xnuspy, jadi sebelum membuka masalah, pastikan Anda masih panik ketika Anda tidak melakukan apa pun selain memanggil fungsi asli dan mengembalikan nilainya (jika perlu). Jika Anda masih panik, maka kemungkinan bug xnuspy (dan buka masalah), tetapi jika tidak, ada yang salah dengan penggantian Anda.
Karena Xnuspy sebenarnya tidak mengarahkan kembali eksekusi ke halaman EL0, men -debug kepanikan tidak begitu mudah. Buka module/el1/xnuspy_ctl/xnuspy_ctl.c , dan tepat sebelum satu -satunya panggilan ke kwrite_instr di xnuspy_install_hook , tambahkan panggilan ke IOSleep selama beberapa detik. Ini dilakukan untuk memastikan ada cukup waktu sebelum perangkat panik agar log menyebar. Kompilasi ulang xnuspy dengan XNUSPY_DEBUG=1 make -B dan muat modul lagi. Setelah memuat modul, jika Anda belum melakukannya, kompilasi klog dari klog/ . Unggah ke perangkat Anda dan lakukan stdbuf -o0 ./klog | grep shared_mapping_kva . Jalankan program hook Anda lagi dan saksikan garis dari klog yang terlihat seperti ini:
shared_mapping_kva: dist 0x7af4 uaddr 0x104797af4 umh 0x104790000 kmh 0xfffffff00c90c000
Jika Anda memasang lebih dari satu kait, akan ada lebih dari satu kejadian. Dalam hal ini, dist dan uaddr akan bervariasi, tetapi umh dan kmh tidak. kmh menunjuk ke awal pemetaan kernel dari segmen __TEXT program Anda. Lemparkan program pengait Anda ke dalam disassembler favorit Anda dan rebase sehingga header Mach-O-nya ada di alamat kmh . Untuk Ida Pro, itu Edit -> Segments -> Rebase program... dengan Image base menggelegak. Setelah perangkat Anda panik dan reboot lagi, jika ada alamat yang sesuai dengan pemetaan kernel dari penggantian Anda di log panik, mereka akan cocok dengan pembongkaran. Jika tidak ada, maka Anda mungkin memiliki semacam korupsi memori halus di dalam pengganti Anda.
Xnuspy juga tidak memiliki cara untuk mengetahui apakah utas kernel masih mengeksekusi (atau akan dieksekusi) pada pemetaan kernel dari segmen __TEXT program Anda setelah kait Anda dihapus. Salah satu hal yang dilakukan Xnuspy untuk menangani ini adalah untuk tidak menangani pemetaan ini segera setelah program kait Anda mati. Sebaliknya, itu ditambahkan ke akhir antrian. Setelah utas pengumpulan sampah Xnuspy memperhatikan batas yang ditetapkan telah terlampaui mengenai berapa banyak halaman pemetaan yang diadakan dalam antrian itu, ia akan mulai menangani oleh bagian depan antrian dan akan berlanjut sampai batas itu tidak lagi terlampaui. Secara default, batas ini adalah 1 MB, atau 64 halaman.
Meskipun ini sangat membantu, semakin besar segmen __TEXT dan __DATA dari program pengait Anda, semakin kecil kemungkinan xnuspy memenangkan perlombaan ini. Jika Anda panik secara teratur dan memiliki program kait yang agak besar, cobalah meningkatkan batas ini dengan menambahkan XNUSPY_LEAKED_PAGE_LIMIT=n sebelum make . Ini akan menetapkan batas ini ke n halaman daripada 64.
Xnuspy menyimpan satu halaman memori kernel statis sebelum xnu boot untuk xnuspy_tramp structs -nya, memungkinkan Anda secara bersamaan mengaitkan sekitar 225 fungsi kernel. Jika Anda menginginkan lebih, Anda dapat menambahkan XNUSPY_TRAMP_PAGES=n sebelum make . Ini akan memberi tahu Xnuspy untuk memesan n halaman memori statis untuk struktur xnuspy_tramp . Namun, jika Xnuspy harus kembali ke kode yang tidak digunakan sudah ada di dalam kernelcache, maka ini diabaikan. Ketika ini terjadi dirinci dalam cara kerjanya.
Untuk beberapa alasan, log dari os_log_with_args tidak muncul di aliran yang dikeluarkan dari alat perintah oslog . Log dari kprintf juga tidak berhasil di sana, tetapi mereka dapat dilihat dengan dmesg . Namun, dmesg bukanlah umpan langsung, jadi saya menulis klog , alat yang menunjukkan log kprintf secara real time. Temukan di klog/ . Saya sangat menyarankan untuk menggunakannya alih -alih spam dmesg untuk pesan kprintf Anda.
Jika Anda open: Resource busy Setelah Menjalankan klog , jalankan perintah ini launchctl unload /System/Library/LaunchDaemons/com.apple.syslogd.plist dan coba lagi.
Sayangnya, Anda tidak akan dapat melihat NSLog IF atm_diagnostic_config=0x20000000 diatur dalam bootArgs XNU. klog tergantung pada argumen boot ini yang hadir. Jika Anda ingin NSLog kembali, hapus argumen boot itu dari pongo_send_command di dalam loader.c .
Xnuspy akan mengelola ini untuk Anda. Setelah proses keluar, semua kait kernel yang dipasang oleh proses itu tidak diinstal dalam waktu satu atau lebih.
Sebagian besar kerangka kerja pengait fungsi memiliki panjang minimum yang membuat fungsi yang diberikan tidak dapat dielakkan. XNUSPY memiliki batas ini hanya jika Anda berencana untuk memanggil fungsi asli dan instruksi pertama dari fungsi yang dikaitkan bukan B . Dalam hal ini, panjang minimum adalah delapan byte. Kalau tidak, tidak ada panjang minimum.
Xnuspy menggunakan X16 dan X17 untuk trampolinnya, jadi fungsi kernel yang mengharapkan mereka yang bertahan di seluruh panggilan fungsi tidak dapat dikaitkan (tidak banyak yang mengharapkan ini). Jika fungsi yang ingin Anda kaitkan dimulai dengan BL , dan Anda berniat untuk memanggil yang asli, Anda hanya dapat melakukannya jika menjalankan fungsi asli tidak memodifikasi X17 .
xnuspy_ctl akan melakukan inisialisasi satu kali saat pertama kali dipanggil setelah boot baru. Ini adalah satu -satunya bagian dari xnuspy yang dapat dipenuhi karena saya tidak dapat secara statis menginisialisasi kunci baca/tulis yang saya gunakan. Setelah panggilan pertama kembali, panggilan masa depan apa pun dijamin aman.
Ini disederhanakan, tetapi menangkap ide utama dengan baik. Kait fungsi dalam xnuspy adalah struktur yang berada pada memori kernel yang dapat ditulis dan dapat dieksekusi. Dalam kebanyakan kasus, ini adalah memori yang dikembalikan oleh alloc_static di dalam pongoos. Itu bisa direbus ke ini:
struct {
uint64_t replacement;
uint32_t tramp[2];
uint32_t orig[10];
};
Di mana replacement adalah alamat virtual kernel (diuraikan pada nanti) dari fungsi penggantian, tramp adalah trampolin kecil yang mengarahkan kembali eksekusi untuk replacement , dan orig adalah trampolin yang lebih besar, lebih rumit yang mewakili fungsi asli.
Salah satu hal pertama yang dilakukan Xnuspy adalah menentukan di mana penggantian EL0 berada di dalam ruang alamat proses panggilan. Ini dilakukan sehingga fungsi kernel dapat dikaitkan dari perpustakaan yang dinamis. Header Mach-O yang sesuai dengan alamat penggantian itu disimpan.
Setelah itu, pemetaan perkernel pengguna bersama dari __TEXT header itu dan segmen __DATA (serta segmen apa pun di antara mereka, jika ada) dibuat. __TEXT dibagikan sehingga Anda dapat menghubungi fungsi lain dari kait Anda. __DATA dibagikan sehingga perubahan pada variabel global terlihat oleh EL1 dan EL0.
Karena pemetaan ini adalah salinan __TEXT dan __DATA satu-ke-satu, mudah untuk mengetahui alamat fungsi penggantian pengguna di atasnya. Mengingat alamat dari header Mach -O Mach -O Calling u , alamat awal pemetaan bersama k , dan alamat fungsi penggantian pengguna r , kami menerapkan formula berikut: replacement = k + (r - u)
Setelah itu, replacement adalah alamat virtual kernel dari fungsi penggantian pengguna pada pemetaan bersama dan ditulis ke struktur kait fungsi. Xnuspy tidak mengarahkan kembali eksekusi ke alamat EL0 dari fungsi penggantian karena itu sangat tidak aman: tidak hanya itu menempatkan kita pada belas kasihan penjadwal, itu tidak memberi kita kendali atas skenario di mana proses dengan kait kernel mati sementara utas kernel masih dieksekusi pada penggantian.
Akhirnya, pemetaan bersama ditandai sebagai dapat dieksekusi dan cabang langsung tanpa syarat ( B ) dikumpulkan. Ini mengarahkan eksekusi ke awal tramp , dan itulah yang menggantikan instruksi pertama dari fungsi kernel yang sekarang terpelajar. Sayangnya, ini membatasi kita dari bercabang ke struktur kait lebih dari 128 MB dari fungsi kernel yang diberikan. Xnuspy memang memeriksa skenario ini sebelum boot dan jatuh kembali ke kode yang tidak digunakan sudah ada di kernelcache untuk struktur kait untuk tinggal jika menemukan bahwa ini bisa terjadi.
Saya melakukan yang terbaik untuk memastikan PatchFinders berfungsi, jadi jika ada sesuatu yang tidak berhasil, silakan buka masalah.