IOS KTRR/CTRR详解
2021-11-27 15:30:11 Author: mp.weixin.qq.com(查看原文) 阅读量:4 收藏

1.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_pagetablesl1 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影响了。

通常来讲,对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

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

_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

系统重启后的第一件事就是锁定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


文章来源: https://mp.weixin.qq.com/s?__biz=Mzg4NjU1NDU4MA==&mid=2247483764&idx=1&sn=1aad5274320ba988861761c9f0b7a30d&chksm=cf96abcff8e122d9d2d1bfb56861668c6aa21fc539e38a2294cfb2cf54131a5c53c7b1477a38&scene=58&subscene=0#rd
如有侵权请联系:admin#unsafe.sh