作者: wzt
本文为作者投稿,Seebug Paper 期待你的分享,凡经采用即有礼品相送! 投稿邮箱:[email protected]
bootstrap_pagetables页表重写
之前内核启动时设置的临时页表_bootstrap_pagetables
, 为了方便使用的是block类型的映射,block映射涵盖的虚拟地址范围非常大,后面对它的映射可能会被ktrr拦截下来,所以需要更改下_bootstrap_pagetables
的页表,改为table类型,同时_bootstrap_pagetables
需要用到的代码地址映射范围只需要到开启mmu为止就可以。这个地址可以通过_bootstrap_instructions
符号来定位。
_arm_vm_init:
ADRL X8, _bootstrap_instructions
AND X21, X8, #0xFFFFFFFFFFFFC000
MOV X0, X21
BL _mmu_kvtop
MOV X20, X0
计算出_bootstrap_instructions
的虚拟地址和物理地址。
ADRL X0, _bootstrap_pagetables ; l1 table
BL _mmu_kvtop
CBNZ X0, loc_FFFFFFF007B69070
_bootstrap_pagetables
为l1 table地址。
MOV X21, X0 ; l1 table
UBFX X19, X20, #0x24, #3 ; '$' ; l1 index
LDR X8, [X0,X19,LSL#3] ; l1 pte
AND X24, X8, #0xFFFFFFFFF000
MOV X0, X24 ; l2 table paddr
BL _phystokv
MOV X22, X0 ; l2 table vaddr
提取l1 index,pte以及l2 table虚拟地址。
ADRP X9, #[email protected]
LDR X23, [X9,#[email protected]]
CBNZ X23, loc_FFFFFFF007B690AC ; l2 index
ADRL X23, _ropagetable_begin
STR X23, [X9,#[email protected]]
UBFX X26, X20, #0x19, #0xB ; l2 index
ADD X8, X23, #4,LSL#12
STR X8, [X9,#[email protected]] ; _ropage_next += 4096
MOV X0, X23
BL _mmu_kvtop
MOV X25, X0 ; new l3 table
提取l2 index以及从_ropage_next
分配一个物理页作为l3 table。
UBFX X27, X20, #0xE, #0xB ; l3 index
提取l3 index。
MOV X0, X21 ; void *
MOV W1, #0x4000 ; size_t
BL _bzero ; clear l1 talbe
清空l1table所有页表项内容。
ORR X8, X24, #3
STR X8, [X21,X19,LSL#3] ; reset l1 pte with 0x3(table & vaild)
然后重新设置_bootstrap_instructions
对应的l1 pte,属性改为table类型。
MOV X0, X22 ; void *
MOV W1, #0x4000 ; size_t
BL _bzero ; clear l2 table
AND X8, X25, #0xFFFFFFFFF000
ORR X8, X8, #3
STR X8, [X22,X26,LSL#3] ; reset l2 talbe pte
清空l2 table所有页表项,然后重新设置_bootstrap_instructions
对应的l2 pte,属性改为table类型。
AND X8, X20, #0xFFFFFFFFC000
MOV X9, #0x40000000000683
ORR X8, X8, X9
STR X8, [X23,X27,LSL#3] ; setup l3 pte
重新设置_bootstrap_instructions
对应的l3 pte,至此_bootstrap_pagetables
已经重新初始化完毕,不在受ktrr影响了。
对devicetree内存属性的改动
通常来讲,对ktrr/ctrr的锁定是在devicetree加载和引用结束后才实施的,但也可能在ktrr/ctrr锁定后,也有对devicetree的引用。因此在内核启动阶段对各个内核数据段进行权限设置时,也要把devicetree所在的区域加入到ktrr/ctrr的保护区域。
_arm_vm_prot_init:
ADRP X21, #[email protected]
STR X8, [X24,#[email protected]]
起初_segEXTRADATA
保存的是_segPRELINKTEXTB
地址,这是kernelcache的最低地址。
BL _SecureDTIsLockedDown
CBZ W0, loc_FFFFFFF007B68054
ADRL X8, _PE_state.deviceTreeHead
LDR X9, [X8]
STR X9, [X24,#[email protected]]
LDR X8, [X8,#(qword_FFFFFFF00772C240 - 0xFFFFFFF00772C230)]
STR X8, [X25,#[email protected]]
调用_SecureDTIsLockedDown
,如果返回1,代表devicetree的加载地址不包含在_segPRELINKTEXTB
内,因此要把devicetree的加载地址重新写入segEXTRADATA,然后在调用_arm_vm_page_granular_prot
做属性调整。
_SecureDTIsLockedDown:
ADRL X20, __mh_execute_header
ADRP X8, #[email protected]
LDR X20, [X8,#[email protected]]
CMP X8, #0
判断_DTRootNode
地址是否小于__mh_execute_header
。
对_update_mdscr的保护
mdscr寄存器是arm调试机制的控制寄存器,开启调试机制可能会绕过ktrr保护,xnu的一个缓解措施是在设置完mdscr后,判断kde位是否开启,如果开启就panic。
__TEXT_EXEC:__text:FFFFFFF008139478 _update_mdscr:
MOV X4, #0
MRS X2, #0, c0, c2, #2
BIC X2, X2, X0
X0保存的是要清楚的bit位。
ORR X2, X2, X1
AND X2, X2, #0xFFFFFFFFFFFFDFFF
MSR #0, c0, c2, #2, X2
X1保存的是要设置的bit位。
ANDS X3, X2, #0x2000
ORR X4, X4, X3
B.NE loc_FFFFFFF008139488
CMP X4, XZR
B.NE loc_FFFFFFF0081394A8
RET
__TEXT_EXEC:__text:FFFFFFF0081394A8
ADRL X0, aMdscrKdeWasSet ; "MDSCR.KDE was set"
B _panic
判断kde位是否开启,如果开启就panic。
Ktrr/ctrr锁定
_kernel_bootstrap->_machine_init->_rorgn_stash_range
_rorgn_stash_range:
ADRP X8, #[email protected]
LDR X19, [X8,#[email protected]]
MOV X0, X19
BL _mmu_kvtop
CBNZ X0, loc_FFFFFFF007B624E8
loc_FFFFFFF007B624E8 ; CODE XREF: _rorgn_stash_range+1D8↑j
ADRP X8, #[email protected]
STR X0, [X8,#[email protected]]
将_segLOWESTRO
地址写入_ctrr_begin
ADRP X8, #[email protected]
LDR X19, [X8,#[email protected]]
CBZ X19, loc_FFFFFFF007B6252C
SUB X8, X0, #1
ADRP X9, #[email protected]
STR X8, [X9,#[email protected]]
如果kernelcache binary存在_segHIGHESTRO
,则将_segHIGHESTRO - 1
存入_ctrr_end
。
loc_FFFFFFF007B6252C ; CODE XREF: _rorgn_stash_range+20C↑j
ADRP X8, #[email protected]
LDR X19, [X8,#[email protected]]
MOV X0, X19
ADRP X8, #[email protected]
LDR X8, [X8,#[email protected]]
ADD X0, X8, X0
SUB X8, X0, #1
ADRP X9, #[email protected]
STR X8, [X9,#[email protected]]
如果kernelcache binary不存在_segHIGHESTRO
,则将_segLASTB+segSizeLAST-1
写入_ctrr_end
。
_rorgn_stash_range
函数只是计算出了_ctrr_begin
和_ctrr_end
,真正对内核锁定是在_rorgn_lockdown
函数。
_kernel_bootstrap_thread->_machine_lockdown->_rorgn_lockdown
_rorgn_lockdown:
ADRP X9, #[email protected]
LDR X9, [X9,#[email protected]]
ADRP X10, #[email protected]
LDR X10, [X10,#[email protected]]
MSR #4, c15, c2, #3, X9 ; S3_4_C15_C2_3
MSR #4, c15, c2, #4, X10 ; S3_4_C15_C2_4
MOV W11, #0x12
MSR #4, c15, c2, #5, X11 ; S3_4_C15_C2_5
MOV W11, #1
MSR #4, c15, c2, #2, X11 ; S3_4_C15_C2_2
将_ctrr_begin
写入S3_4_C15_C2_3
寄存器,它保存的是ctrr保护的起始地址,将_ctrr_end
写入S3_4_C15_C2_4
,它保存的是ctrr保护的结束地址。
将0x12写入S3_4_C15_C2_5
寄存器,它保存的是ctrr的控制状态。Xnu source code里已经有对它的描述:
Apple_arm64_regs.h
\#define CTRR_CTL_EL1_A_MMUOFF_WRPROTECT (1 << 0)
\#define CTRR_CTL_EL1_A_MMUON_WRPROTECT (1 << 1)
\#define CTRR_CTL_EL1_B_MMUOFF_WRPROTECT (1 << 2)
\#define CTRR_CTL_EL1_B_MMUON_WRPROTECT (1 << 3)
\#define CTRR_CTL_EL1_A_PXN (1 << 4)
\#define CTRR_CTL_EL1_B_PXN (1 << 5)
\#define CTRR_CTL_EL1_A_UXN (1 << 6)
\#define CTRR_CTL_EL1_B_UXN (1 << 7)
将1写入S3_4_C15_C2_2
寄存器,锁定ctrr。
MRS X11, #0, c4, c2, #2 ; CurrentEL
CMP X11, #8 ; el2
B.NE loc_FFFFFFF007B62DEC
MSR #4, c15, c11, #0, X9 ; S3_4_C15_C11_0
MSR #4, c15, c11, #1, X10 ; S3_4_C15_C11_1
MOV W9, #0x12
MSR #4, c15, c11, #4, X9 ; S3_4_C15_C11_4
MOV W9, #1
MSR #4, c15, c11, #5, X9 ; S3_4_C15_C11_5
可以看到ctrr除了能对el1内核代码做保护,还可以对el2 hypervisor做保护。
S3_4_C15_C11_0
代表要保存的el2代码起始地址,S3_4_C15_C11_1
代表结束地址,S3_4_C15_C11_4
代表控制寄存器,S3_4_C15_C11_5
代表锁定寄存器。
Ktrr/ctrr保护的区域范围如下:
\* __PRELINK_TEXT <--- First KTRR (ReadOnly) segment
\* __PLK_DATA_CONST
\* __PLK_TEXT_EXEC
\* __TEXT
\* __DATA_CONST
\* __TEXT_EXEC
\* __KLD
\* __LAST <--- Last KTRR (ReadOnly) segment
\* __DATA
\* __BOOTDATA (if present)
\* __LINKEDIT
\* __PRELINK_DATA (expected populated now)
\* __PLK_LINKEDIT
\* __PRELINK_INFO
_reset_vector的行为
系统重启后的第一件事就是锁定ktrr/ctrr寄存器。
_reset_vector:
ADRL X17, _ctrr_begin
LDR X17, [X17]
CBZ X17, loc_FFFFFFF00813445C
ADRL X19, _ctrr_end
LDR X19, [X19]
CBZ X19, loc_FFFFFFF00813446C
MRS X18, #4, c15, c2, #2
CBNZ X18, loc_FFFFFFF0081344A8
MSR #4, c15, c2, #3, X17 ; S3_4_C15_C2_3
MSR #4, c15, c2, #4, X19 ; S3_4_C15_C2_4
MOV X18, #0x12
MSR #4, c15, c2, #5, X18 ; S3_4_C15_C2_5
MOV X18, #1
MSR #4, c15, c2, #2, X18 ; S3_4_C15_C2_2
本文由 Seebug Paper 发布,如需转载请注明来源。本文地址:https://paper.seebug.org/1769/