IOS KTRR/CTRR 详解
2021-11-29 11:11:00 Author: paper.seebug.org(查看原文) 阅读量:25 收藏

作者: 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

Paper 本文由 Seebug Paper 发布,如需转载请注明来源。本文地址:https://paper.seebug.org/1769/



文章来源: http://paper.seebug.org/1769/
如有侵权请联系:admin#unsafe.sh