AMCC (Apple Memory Cache Controller) RoRgn¶
Apple'ın memory controller'ı (AMCC) tarafından zorlanan hardware read-only region: controller'ın tüm write'ları sessizce düşürdüğü bir DRAM page range; bu KTRR'a, kernel code'unun ve read-only data'sının asla değiştirilemeyeceğine dair ikinci, MMU-independent bir garanti verir.
Mechanism¶
Invariant
Software W^X korumaları (page-table permission bit'leri), page table'lara ya da
translation'ı yöneten system register'lara erişebilen herkes tarafından geri
alınabilir. KTRR bu boşluğu, read-only kernel region'ını iki bağımsız hardware
katmanında zorlayarak kapatır. Bir katman CPU'nun MMU'sunda yaşar (KTRR_LOWER_EL1
/ KTRR_UPPER_EL1 / KTRR_LOCK_EL1 register'ları, EL1'deki instruction fetch'leri
locked range dışında fail etmeye zorlar). Diğer katman AMCC'dir — CPU ile DRAM
arasında oturan memory controller. AMCC fiziksel bir Read-Only Region (RoRgn) ile
konfigüre edilir: start page, end page ve bir lock. Bir kez locked olunca controller,
page table'lardan bağımsız, EL'den bağımsız ve hangi core'un issue ettiğinden
bağımsız olarak, fiziksel adresi region içine düşen her write'ı reddeder. Check
MMU'nun altında olduğu için, hiçbir miktarda page-table ya da translation-register
manipülasyonu kernel text'e başarılı bir write üretemez. Lock tek yönlüdür: lock
register yazıldıktan sonra base/end/lock alanları bir sonraki reset'e kadar
immutable'dır.
RoRgn, kernelcache'in contiguous read-only span'ini kapsar; __PRELINK_TEXT
segment'inden __LAST'a kadar. Executable subset (MMU'nun ayrıca r-x olarak
işaretlediği şey) __PRELINK_TEXT'ten __TEXT_EXEC'e kadar uzanır; __LAST.__pinst
RoRgn'in içinde oturur ama MMU açılınca non-executable yapılır. KTRR, boot'un erken
aşamasında iBoot/XNU tarafından kurulur ve untrusted code çalışmadan önce locked
edilir.
Walkthrough¶
RoRgn, AMCC register block'undaki düz MMIO'dur. Siguza'nın KTRR analizine göre üç alan, AMCC base'inden sabit offset'lerde yaşar:
| Field | Offset | Meaning |
|---|---|---|
rRORGNBASEADDR |
+0x7e4 | region'ın start page number'ı |
rRORGNENDADDR |
+0x7e8 | region'ın end page number'ı |
rRORGNLOCK |
+0x7ec | üç alanı da lock'lamak için 1 yaz |
XNU'nun lockdown sequence'ı kavramsal olarak şöyledir:
// illustrative — physical writes to the AMCC MMIO block (amcc_base)
rRORGNBASEADDR = atop(rorgn_begin); // __PRELINK_TEXT, page number
rRORGNENDADDR = atop(rorgn_end); // __LAST, page number
rRORGNLOCK = 1; // one-way lock: fields now frozen
__asm__ volatile("isb sy");
// then the MMU side:
__builtin_arm_wsr64("ARM64_REG_KTRR_LOWER_EL1", rorgn_begin);
__builtin_arm_wsr64("ARM64_REG_KTRR_UPPER_EL1", rorgn_end);
__builtin_arm_wsr64("ARM64_REG_KTRR_LOCK_EL1", 1);
Lock'tan sonra iki katman şöyle davranır:
- AMCC katmanı:
[rRORGNBASEADDR, rRORGNENDADDR]aralığındaki herhangi bir fiziksel adrese yapılan bir store, controller tarafından kabul edilir ve atılır — write sessizce yok olur, DRAM'deki data değişmez. Hiçbir fault olmaz. - MMU/KTRR katmanı: MMU enable iken EL1'de, KTRR executable range dışındaki bir adresi hedefleyen bir instruction fetch fault verir, dolayısıyla başka yere inject edilmiş code kernel olarak çalışamaz.
Lock'u bir kernel R/W primitive ile doğrulama
Yaygın bir bring-up check'i (jailbreak/RE tooling'inde kullanılan), bir physical-write primitive elde etmek, bilinen bir kernel-text page'ine bir sentinel yazmak ve sonra onu geri okumaktır:
// pseudo: with kernel phys read/write
pa = kvtophys(addr_in_kernel_text);
phys_write64(pa, 0x4141414141414141);
val = phys_read64(pa);
// val == original instruction, NOT 0x41414141... -> AMCC dropped the write
Başarılı bir write, RoRgn lock'unun devreye girmediği anlamına gelirdi (örn. patch'lenmemiş pre-KTRR bir cihaz ya da RoRgn'siz bir controller). KTRR-enforcing cihazlarda write bir no-op'tur.
Bu bir software toggle değildir
KTRR/CTRR hardware'inde, compromise olmuş bir EL1 kernel'ın lock'tan sonra write'ları yeniden enable etmek için flip edebileceği bir register yoktur. Bypass'lar lock'tan önceki configuration window'unu (örn. boot-chain compromise) ya da hardware glitching'i hedef alır — locked state'i değil.
Detection¶
- Kernel text'e yapılan ve değişmemiş olarak geri okunan bir physical write (yukarıdaki sentinel test'i), AMCC RoRgn'in aktif olduğunu doğrular.
- A12+ üzerinde eşdeğer in-core memory-controller koruması CTRR register'ları üzerinden açığa çıkar; aynı "write'lar düşürülür, range dışı fetch'ler fault verir" davranışı geçerlidir.
Mitigation¶
RoRgn zaten mitigation'dır. MMU tarafını değiştirmez, tamamlar: AMCC, DRAM'deki
data-at-rest'i savunur (write yok) ve KTRR'ın MMU register'ları execution'ı savunur
(unlocked page'lerin EL1 code olarak fetch edilmemesi). İkisi de boot'un erken
aşamasında tek yönlü locked edildiği için, tek başına runtime kernel R/W kernel text'i
yeniden açamaz — bitişik per-page / pointer-integrity katmanları için aprr ve pac'e,
pre-lock boot window'unu hedef alan saldırılar için secure-boot-bypass'a bak.