作者: wzt
本文為作者投稿,Seebug Paper 期待你的分享,凡經采用即有禮品相送! 投稿郵箱:paper@seebug.org

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, #_ropage_next@PAGE

LDR       X23, [X9,#_ropage_next@PAGEOFF]

CBNZ       X23, loc_FFFFFFF007B690AC ; l2 index

ADRL       X23, _ropagetable_begin

STR       X23, [X9,#_ropage_next@PAGEOFF]

UBFX       X26, X20, #0x19, #0xB ; l2 index

ADD       X8, X23, #4,LSL#12

STR       X8, [X9,#_ropage_next@PAGEOFF] ; _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, #_segPRELINKTEXTB@PAGE

STR       X8, [X24,#_segEXTRADATA@PAGEOFF]

起初_segEXTRADATA保存的是_segPRELINKTEXTB地址,這是kernelcache的最低地址。

BL        _SecureDTIsLockedDown

CBZ       W0, loc_FFFFFFF007B68054

ADRL       X8, _PE_state.deviceTreeHead

LDR       X9, [X8]

STR       X9, [X24,#_segEXTRADATA@PAGEOFF]

LDR       X8, [X8,#(qword_FFFFFFF00772C240 - 0xFFFFFFF00772C230)]

STR       X8, [X25,#_segSizeEXTRADATA@PAGEOFF]

調用_SecureDTIsLockedDown,如果返回1,代表devicetree的加載地址不包含在_segPRELINKTEXTB內,因此要把devicetree的加載地址重新寫入segEXTRADATA,然后在調用_arm_vm_page_granular_prot做屬性調整。

_SecureDTIsLockedDown:

ADRL       X20, __mh_execute_header

ADRP       X8, #_DTRootNode@PAGE

LDR       X20, [X8,#_DTRootNode@PAGEOFF]

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, #_segLOWESTRO@PAGE

LDR       X19, [X8,#_segLOWESTRO@PAGEOFF]

MOV       X0, X19

BL        _mmu_kvtop

CBNZ       X0, loc_FFFFFFF007B624E8

loc_FFFFFFF007B624E8           ; CODE XREF: _rorgn_stash_range+1D8↑j

ADRP       X8, #_ctrr_begin@PAGE

STR       X0, [X8,#_ctrr_begin@PAGEOFF]

_segLOWESTRO地址寫入_ctrr_begin

ADRP       X8, #_segHIGHESTRO@PAGE

LDR       X19, [X8,#_segHIGHESTRO@PAGEOFF]

CBZ       X19, loc_FFFFFFF007B6252C

SUB       X8, X0, #1

ADRP       X9, #_ctrr_end@PAGE

STR       X8, [X9,#_ctrr_end@PAGEOFF]

如果kernelcache binary存在_segHIGHESTRO,則將_segHIGHESTRO - 1存入_ctrr_end

loc_FFFFFFF007B6252C           ; CODE XREF: _rorgn_stash_range+20C↑j

ADRP       X8, #_segLASTB@PAGE

LDR       X19, [X8,#_segLASTB@PAGEOFF]

MOV       X0, X19

ADRP       X8, #_segSizeLAST@PAGE

LDR       X8, [X8,#_segSizeLAST@PAGEOFF]

ADD       X0, X8, X0

SUB       X8, X0, #1

ADRP       X9, #_ctrr_end@PAGE

STR       X8, [X9,#_ctrr_end@PAGEOFF]

如果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, #_ctrr_begin@PAGE

LDR       X9, [X9,#_ctrr_begin@PAGEOFF]

ADRP       X10, #_ctrr_end@PAGE

LDR       X10, [X10,#_ctrr_end@PAGEOFF]

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

Paper 本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/1769/