IOS SPTM深度分析

· 2026-01-02 17:57 · 3 阅读

原创 wzt 2026-01-02 17:57 浙江

本文深度分析了IOS SPTM代码实现细节

1 SPTM启动

2 GL Entry分析

3 Exception处理分析

4 Sptm页表保护

5 Sptm retype分类保护

6 TXM分析

1. sptm启动

    MRS X0, #0, c4, c2, #2 ; CurrentEL
    CMP X0, #8
    B.NE loc_FFFFFFF00706B074 ; != EL2
    MRS X0, #4, c1, c1, #0 ; HCR_EL2
    ORR X0, X0, #0x8000000 ; TGE, bit [27] All exceptions that would be routed to EL1 are routed to EL2.
    ORR X0, X0, #0x400000000 ; E2H, bit [34], The facilities to support a Host Operating System at EL2 are enabled.
    MSR #4, c1, c1, #0, X0 ;

        首先判断如果在el2状态,则设置hcr_el2TGEE2H状态。 sptmiboot加载,iboot跳转到sptm时处于el2状态。

      ADRL X30, sptm_init_subsystem
      MOV X21, X20
      ADRL X0, unk_FFFFFFF0070B0000
      ADD X0, X0, X22
      SUB X0, X0, X23
      MSR #5, #0
      MOV SP, X0
      MOV X24, #1
      B init_hardware_registers ;

         执行init_hardware_registers,后在跳转到sptm_init_subsystem继续执行。

      init_hardware_registers的执行流程:

      设置SPRR_PPERM_EL1寄存器为0x2020a52a302afaf5

      设置TCR_EL10x0800236511a511

      设置TTBR0_EL10xFFFFFFF007010000

      设置TTBR1_EL10xFFFFFFF0070A0000

      重新设置APCTL_EL1

      0xFEEDFACEFEEDFACF依次设置APIBKey_EL1APDBKey_EL1KERNKE_EL1(苹果新增)、 APIAKey_EL1APDAKey_EL1APGAKey_EL1

      设置SCTLR_EL10x01000fc14713d

      设置若干HID*寄存器(苹果新增)。

      sptm_init_subsystem的执行流程:

      调用copy_and_clear_random_seed生成若干随机种子。

      调用init_xnu_ro_data初始化xnu_ro_pagetables_beginxnu_ro_pagetables_end

      xnu_exc_return_handlerxnu_ctrr_dispatch_table等全局变量。

      调用validate_region_order检查txm的地址范围。

      调用register_papt_range依次注册SPTM-rxSPTM-rwSPTM-leTXM-roTXM-rxTXM-bxTXM-rwBootKC-roBootKC-rsBootKC-rxBootKC-bxBootKC-rwBootKC-leTrustCacheCL4-roCL4-rxCL4-rwCL4-leRAMDiskRTBuddySegSEPFWSEPPatchesuStuffpreoslogBootArgsExclaveOSIntegrityCatalogExclaveOSTrustCacheAVAILABLEPHYS_SLIDE等地址范围信息。

        register_papt_range(char *name, uint64_t type, uint64_t flag)
        {
           init_get_image_region(name);
           bootstrap_register_papt_range();
        }

        bootstrap_register_papt_rangepapt_range_array数组注册一个地址范围信息:

          struct papt_range_array {
             char *name;
          +0x8:
             uint64_t type;
          +0x10:
             uint64_t start_address;
          +0x18:
             uint64_t base;
          +0x20:
             uint64_t end_address;
          +0x24:
             char flag;
          +0x28:
          };

            bootstrap_register_papt_range(char *name, uint64_t type, uint64_t start_addr, uint64_t end_addr, uint64_t flag)
            {
              papt_range_array[papt_range_array_length].name = name;
              papt_range_array[papt_range_array_length].type = type;
              papt_range_array[papt_range_array_length].start_address = start_addr;
              papt_range_array[papt_range_array_length]end_address = end_addr;
              papt_range_array[papt_range_array_length].flag = flag;
              papt_range_array_length++






            Name

            Type

            Flag

            DeviceTree

            0xb

            0x20

            SPTM-ro

            0x2

            0x28

            SPTM-rx

            0x4

            0x21

            SPTM-rw

            0x2

            0x21

            SPTM-le

            0x2

            0x21

            TXM-ro

            0x20

            0x48

            TXM-rx

            0x20

            0x41

            TXM-bx

            0x5

            0x41

            TXM-rw

            0x20

            0x41

            TXM-le

            0x20

            0x41

            BootKC-ro

            0xb

            0x48

            BootKC-rs

            0xb

            0x41

            BootKC-rx

            0xb

            0x41

            BootKC-bx

            x

            0x41

            BootKC-rw

            0xb

            0x51

            BootKC-le

            0xb

            0x51

            TrustCache

            0x0

            0x40

            CL4-ro

            0x32

            0x6

            CL4-rx

            0x32

            0x6

            CL4-rw

            0x32

            0x6

            CL4-le

            0x32

            0x6

            RAMDisk

            0xb

            0x2

            RTBuddySeg

            0xb

            0x2

            SEPFW

            0xb

            0x2

            SEPPatches

            0xb

            0x2

            uStuff

            0xb

            0x2

            Preoslog

            0xb

            0x2

            BootArgs

            0xb

            0x0

            ExclaveOSIntegrityCatalog

            0x32

            0x6

            ExclaveOSTrustCache

            0x32

            0x6

            AVAILABLE

            0x0

            0x0

            PHYS_SLIDE

            0xb

            0x0

            调用sptm_bootstrap_early

            调用初始化sprr相关寄存器:

            设置SPRR_PPERM_EL1 - sprr kernel permission寄存器值为0x2020a52a302afaf5

            设置SPRR_UPERM_EL0 - sprr user permission寄存器值为0x2010002030100000

            调用init_gxf_mode_prev

            设置GXF_CONFIG_EL1寄存器为1

            设置GXF_PABENTRY_EL1

            设置GXF_ENTRY_EL1sub_FFFFFFF00706C808

            执行0x201420指令进入Guard Level模式, GL的入口地址设置为sub_FFFFFFF00706C808

              __text:FFFFFFF00706C808 sub_FFFFFFF00706C808 ; DATA XREF:
              MSR #6, c15, c11, #1, X0 ; TPIDR_GL2 - Software Thread ID Register (GL2)
              MOVK X0, #0x2020,LSL#48
              MOVK X0, #0xA52A,LSL#32
              MOVK X0, #0x302A,LSL#16
              MOVK X0, #0xFAE6 ; 0x2020a52a302afae6
              MSR #6, c15, c1, #6, X0 ; SPRR_PPERM_EL1 - SPRR Kernel Permission Configuration Register (EL1)
              MOVK X0, #0,LSL#48
              MOVK X0, #0,LSL#32
              MOVK X0, #0,LSL#16
              MOVK X0, #0xFF
              MSR #6, c15, c1, #0, X0 ; SPRR_CONFIG_EL1 - SPRR Configuration Register (EL1)
              RET

              首先设置TPIDR_GL2寄存器为init_gl2_thread

                gl2_thread {
                +0x0:
                   char domain_type;
                +0x8:
                   uint64_t tls_index;
                +0x18:
                   uint64_t gl2_sp;
                +0x20:
                   uint64_t unknown;
                +0x28:
                   char sptm_state;
                +0x30:
                   uint64_t hop_flag;
                +0x38:
                 struct save_caller_reg {
                   uint64_t x0,x1,x2,x3,x4,x5,x6,x7;
                };
                +0x78:
                 struct save_exception_reg {
                   uint64_t 0x0;
                   ...
                   uint64_t 0xb0;
                 };
                +0xf8:
                   uint64_t gl1_sp;
                +0x130:
                 struct save_exception_reg {
                   uint64_t 0x0;
                   ...
                   uint64_t 0xb0;
                 };
                +0x1f0:
                 uint64_t gl0_sp;
                +0x520:
                 union {
                 struct config_reg {
                   uint64_t save_caller_arg_address;
                   uint64_t sprr_user_permission;
                   uint64_t id_isar2_el1;
                   uint64_t unknown;
                   uint64_t apctl_el1;
                   uint64_t sctlr_el1;
                 };
                 struct config_reg1 {
                   uint64_t tpidr_el0;
                   uint64_t tpidrro_el0;
                   uint64_t sprr_pperm_el12;
                   uint64_t sprr_uperm_el02
                   uint64_t gxf_config_el12;
                   uint64_t sprr_config_el12;
                   ...
                   uint64_t tpidr_gl1;
                   uint64_t unknown;
                   uint64_t vbar_gl12;
                   uint64_t unknown;
                +0x88:
                   uint64_t hcr_el2;
                 };
                +0x5b0:
                 strucct config_reg2 {
                   uint64_t;
                 +0x88:
                   uint64_t;
                 };
                +0x640:
                 struct unknown_reg_store {
                   uint64_t x0,x1,x2,x3,x4,x5,x6,x7;
                 };
                +0x668:
                 char el_level;
                +0x680:
                 uint64_t current_cpu;
                +0x7f0:
                 uint64_t value;
                +0x7f8:
                 uint64_t unknown;
                +0x870:
                 struct unknown_ctrr_struct *ctrr;
                };
                struct unknown_ctrr_struct {
                 uint64_t value1;
                +0x800:
                 uint64_t value2;
                };

                    重新设置SPRR_PPERM_EL1 - sprr kernel permission寄存器值为0x2020a52a302afae6

                    重新设置SPRR_CONFIG_EL10xff

                注意随后使用的是ret指令,而不是0x201400,说明并没有推出GL模式,从而让sptm从普通的el2模式进入到GL2模式继续运行。

                调用init_sptm_mode继续初始化sptm

                锁定VMSA_LOCK_EL1寄存器。

                CTRR_A_UPR/LOW_EL1寄存器锁定el1地址范围。

                CTRR_A_UPR/LOW_EL2寄存器锁定el2地址范围。

                调用init_gxf_mode_final,它将GXF_ENTRY_EL1设置为gxf_entry_el1函数地址,将VBAR_GL1设置为syn_handler_sp0_g12地址,将GXF_CONFIG_EL1重新设置为0x2d

                分别设置elr_gl1_cl4elr_gl1_txmelr_gl1_kernelcache三个全局变量。

                调用sptm_register_dispatch_table注册当前domain的三个dispatch_table函数。




                table_id

                Dispatch function

                Table_permissions

                0

                sptm_dispatch

                2

                1

                sptm_dispatch

                4

                2

                sptm_dispatch

                8

                sptm_register_dispatch_table(uint64_t table_id, uint64_t dispatch_entry_point, uint64_t table_permissions)gl2_thread->domain_type < 2时选择xnu_ctrr_dispatch_table,否则选择domain_id_array

                  domain_id_struct {
                   table_id_struct {
                   uint64_t entry_point;
                   uint64_t table_permissions;
                   uint64_t value2;
                   }[16];
                  }[16];

                    domain_id_array[gl2_thread->domain_type][table_id].entry_point = dispatch_entry_point;
                    domain_id_array[gl2_thread->domain_type][table_id].table_permissions = table_permissions;

                    至此sptm初始化完毕。

                    2 GL Entry分析

                    2.1 gxf_entry_el1分析

                    Kernelcachesptm启动阶段的代码会调用0x201420指令进入Guard Level模式,在sptm的启动阶段将gxf_entry_el1写入GXF_ENTRY_EL1寄存器,通过x16对其传递参数。

                    除了0x201420指令,在kernelcache代码中,还看到了其他三种指令也能进入GL模式:

                      com.apple.kernel:__text:FFFFFFF0284F63F0 sptm_txm_call ; CODE XREF: com.apple.kernel:__text:FFFFFFF028266D8C↑p
                      PACIBSP
                      MOV W16, W0
                      MOVK X16, #2,LSL#48
                      MOVK X16, #0,LSL#32 ; 0x20000w0000000
                      MOV X10, X1
                      LDP X0, X1, [X10]
                      LDP X2, X3, [X10,#0x10]
                      LDP X4, X5, [X10,#0x20]
                      LDP X6, X7, [X10,#0x30]
                      DCD 0x201420
                      RETAB
                      DCD 0x201421
                      DCD 0x201422
                      DCD 0x201423

                      在对gxf_entry_el1代码的追踪中,发现只有0x201420可以向sptm传递参数,其他三个只能调用特定的几个dispatch函数。

                        gxf_entry_el1(uint64_t event_metadata)
                        {
                           uint64_t esr_gl1 = msr(ESR_GL1) & 0x1f;        // genter arg
                           if (!(word_FFFFFFF007094018 & 0xf)) {
                             if (word_FFFFFFF007094018 != gl2_thread->0x680) {
                               dispatch_state_machine(0xb0);    
                             }
                             else {
                               if (esr_gl1 == 0) {
                                 if (gl2_thread->unkown_flag != 0)
                                   dead_loop();

                                 uint64_t domain_id = (metadata >> 48) & 0xffff;
                                 if (domain_id == 0)
                                   dispatch_state_machine(2, event_metadata);
                                 else if (domain_id == 3)
                                   dispatch_state_machine(4, event_metadata);
                                 else if (domain_id == 2)
                                   dispatch_state_machine(3, event_metadata);
                                 else
                                 genter_dispatch_entry_error();
                             }
                             else if (esr_gl1 == 1) {
                               if (gl2_thread->unkown_flag != 0)
                               dead_loop();
                               dispatch_state_machine(0xa0);
                             }
                             else {
                               genter_panic();
                             }
                           }
                         }
                         else {
                           if (esr_gl1 == 0) {
                             if (gl2_thread->unkown_flag != 0)
                               dead_loop();

                               uint64_t domain_id = (metadata >> 48) & 0xffff;
                               if (domain_id == 0)
                                 dispatch_state_machine(2, event_metadata);
                               else if (domain_id == 3)
                                 dispatch_state_machine(4, event_metadata);
                                else if (domain_id == 2)
                                 dispatch_state_machine(3, event_metadata);
                               else
                                 genter_dispatch_entry_error();
                               }
                             else if (esr_gl1 == 1) {
                               if (gl2_thread->unkown_flag != 0)
                                 dead_loop();
                                 dispatch_state_machine(80);
                             }
                             else if (esr_gl1 == 2) {
                               if (gl2_thread->unkown_flag != 0)
                                 dead_loop();
                                 dispatch_state_machine(30x200000000000);
                             }
                             else if (esr_gl1 == 3) {
                               if (gl2_thread->unkown_flag != 0)
                                 dead_loop();
                                 dispatch_state_machine(40x3000000000000);
                               }
                               else {
                                 dead_loop();
                             }
                           }
                        }

                        0x201420可以触发如下调用路径,可以传递参数:

                          dispatch_state_machine(2, event_metadata);
                          dispatch_state_machine(4, event_metadata);
                          dispatch_state_machine(3, event_metadata);

                          0x201421可以触发如下调用路径,不能传递参数:

                            dispatch_state_machine(0xa, 0);
                            dispatch_state_machine(80);

                            0x201422可以触发如下调用路径,不能传递参数:

                            dispatch_state_machine(3, 0x200000000000);

                            0x201423可以触发如下调用路径,不能传递参数:

                            dispatch_state_machine(4, 0x3000000000000);

                            2.2 dispatch_state_machine分析

                            dispatch_state_machine根据sptm的状态数据库sptm_state_array进行不同的操作。

                            sptm_state_array是一个二维数组:

                              sptm_state_struct {
                                dispatch_table_struct {
                                  char next_state;
                                  void *func_addr;
                                  char domain_type;
                                  +0x18:
                                    uint64_t unknown_value;
                                  +0x20:
                                }[12];
                                +0x180:
                              }[16];

                                  通过gl2_thread->sptm_state定位一维数组索引,通过第一个参数event_type定位二维数组索引。

                              1705907103518

                                  Func_addr为不同sptm state状态的驱动函数。

                              1705907377889

                                  在对sptm_state_array这个庞大的状态数组进行分析后,得到了所有的状态驱动函数:

                                (gdb) p/x 0x598E0+0x180
                                $3 = 0x59a60
                                (gdb) p/x 0x5D548+0x4000
                                $4 = 0x61548
                                (gdb) p/x 0x5C370+0x4000
                                $5 = 0x60370
                                (gdb) p/x 0x5D04C+0x4000
                                $6 = 0x6104c
                                (gdb) p/x 0x5D098+0x4000
                                $7 = 0x61098
                                (gdb) p/x 0x5CCFC+0x4000
                                $8 = 0x60cfc
                                (gdb) p/x 0x5CD8C+0x4000
                                $9 = 0x60d8c
                                (gdb) p/x 0x5C0E4+0x4000
                                $10 = 0x600e4
                                (gdb) p/x 0x5D57C+0x4000
                                $11 = 0x6157c
                                (gdb) p/x 0x5C47C+0x4000
                                $12 = 0x6047c
                                (gdb) p/x 0x5C294+0x4000
                                $13 = 0x60294
                                (gdb) p/x 0x5D44C+0x4000
                                $14 = 0x6144c
                                (gdb) p/x 0x85DA0+0x4000
                                $15 = 0x89da0
                                (gdb) p/x 0x5CEE0+0x4000
                                $16 = 0x60ee0
                                (gdb) p/x 0x5C8E0+0x4000
                                $17 = 0x608e0
                                (gdb) p/x 0x5D32C+0x4000
                                $18 = 0x6132c
                                (gdb) p/x 0x5D364+0x4000
                                $19 = 0x61364

                                对其重新命名:

                                1705907543440

                                    接着对参数event_metadata进行提取domain_typetable_idevent_metadata48-53位保存domain_type 32-39保存table_id

                                1705907904374

                                然后测试提取出的domain_type有没有这个table_id的对应权限。

                                1705907997927

                                    最终提取的是table_id_struct里的entry_point函数,接着调用sptm state的驱动函数,将这个entry_point作为回调参数:

                                1705908248510

                                    Entry_pointsptm启动阶段通过sptm_register_dispatch_table进行了注册:

                                1705908414664

                                    IOMMU子系统也进行了对应的注册:

                                1705908502322

                                 在iommu_register_dispatch_table函数内部也是将sptm_dispatch作为entry_point进行注册:

                                1705908570552

                                   在分析Sptm_dispatch之前,还需要弄清sptm state驱动函数在何时调用sptm_dispatch

                                Sptm在不同的状态将会调用不同的驱动函数,sptm_state_func1函数只会将elr_gl1设置为elr_gl1_cl4,返回gl0运行,不会调用sptm_dispatchsptm_state_func2函数同上,在gl0运行elr_gl1_txm。而sptm_state_func3函数才会在gl2调用sptm_dispatch:

                                1705909920023

                                    X1就是驱动函数的第2个参数,即sptm_dispatch

                                sptm_state_func8比较有意思,它将sptm_dispatch放到了gl0运行:

                                1705912645964

                                    完整的dispatch_state_machine函数如下:

                                  voiddispatch_state_machine(char event_type, uint64_t event_metadata)
                                  {
                                     check_GXF_STATUS_EL1();
                                     if (gl2_thread->stpm_state > 0x11)
                                       invalid_state_branch();
                                     if (event_type > 0xc)
                                       invalid_event_type_branch();

                                       struct sptm_state_struct *sptm_state = sptm_state_array[gl2_thread->sptm_state * 0x180];
                                       struct dispatch_table_struct *target_elem = sptm_state_array[gl2_thread->sptm_state * 0x180][event_type * 32];
                                       if (target_elem->next_state > 0x11)
                                           invalid_next_state_branch();
                                       if (!target_elem->func_addr)
                                           invalid_state_transition_branch();
                                       gl2_thread->domain_type = target_elem->domain_type;
                                       if (!(target_elem->unknown_value & 0x1)) {
                                         if (!(target_elem->unknown_value & 0x2)) {
                                           gl2_thread->sptm_state = target_elem->next_state;
                                           target_elem->func_addr(event_metadata, NULL);
                                         }
                                         else {
                                           if (gl2_thread->hop_flag != 1)
                                             invalid_hop_detected_branch();

                                             dispatch_err_log();
                                         }
                                     }
                                     else {
                                       if (event_metadata & 0xFC000000000000)
                                           domain_id_branch();
                                       if (event_metadata & 0xF000000000)
                                           table_id_branch();   
                                        uint64_t domain_id_index = (event_metadata >> 48) & 0xff - 1;
                                        uint64_t table_id_index = (event_metadata >> 32) & 0xff
                                       if (domain_id > 1) {
                                         struct domain_id_struct *domain_id = domain_id_array[domain_id_index];
                                         struct table_id_struct *table_id = domain_id_array[domain_id_index][table_id_index * 0x18];
                                       }
                                       else {
                                         struct domain_id_struct *domain_id = domain_id_array[domain_id_index];
                                         struct table_id_struct *table_id = domain_id_array[domain_id_index][table_id_index * 0x18];
                                     }
                                     if (!(table_id->table_permissions >> gl2_thread->caller_domain) & 0x1))
                                       table_permissions_branch();
                                       if (table_id->entry_point == 0)
                                         illegal_dispatch_branch();
                                       if (!(target_elem->unknown_value & 0x2)) {
                                         gl2_thread->sptm_state = target_elem->next_state;
                                         target_elem->func_addr(event_metadata, table_id->entry_point);
                                     }
                                     else {
                                       if (gl2_thread->hop_flag != 1)
                                         invalid_hop_detected_branch();

                                         dispatch_err_log();
                                       }
                                     }
                                  }

                                  2.3 sptm_dispatch

                                  sptm注册了7个子系统,每个子系统都有一个dispatch table

                                  编号

                                  子系统名称

                                  Dispatch table函数表数目

                                  0

                                  Xnu

                                  22

                                  1

                                  txm

                                  5

                                  2

                                  unknown

                                  3

                                  3

                                  dart

                                  17

                                  4

                                  unknown

                                  2

                                  5

                                  sart

                                  3

                                  6

                                  uat

                                  13

                                  函数原型:sptm_dispatch(uint64_t event_metadata)

                                  event_metadata32-39位保存的是dispatch table表中的索引。

                                  使用以下脚本将table表中的值转为对应的函数地址:

                                    awk '{print $3}' c.txt|sed -e 's/8[0-9][0-9]/0/' |awk '{a=strtonum($1)+0x4000+0x7000000; printf("%d 0xfffffff0%08x\n", NR, a)}'

                                    XNU

                                    函数地址

                                    函数名

                                    1

                                    0xfffffff007072b9c

                                    unknown

                                    2

                                    0xfffffff00708a2ec

                                    sptm_retype

                                    3

                                    0xfffffff00708a824

                                    sptm_map_page

                                    4

                                    0xfffffff00708b2d8

                                    sptm_map_table

                                    5

                                    0xfffffff00708ba34

                                    sptm_unmap_table

                                    6

                                    0xfffffff00708cd58

                                    sptm_update_region

                                    7

                                    0xfffffff00708d350

                                    sptm_update_disjoint

                                    8

                                    0xfffffff00708c95c

                                    sptm_unmap_region

                                    9

                                    0xfffffff00708d3f0

                                    sptm_unmap_disjoint

                                    10

                                    0xfffffff00708d830

                                    sptm_shared_region_configure

                                    11

                                    0xfffffff00708db74

                                    sptm_nest_region

                                    12

                                    0xfffffff00708e2bc

                                    sptm_unnest_region

                                    13

                                    0xfffffff00708e6f8

                                    sptm_validate_root_config

                                    14

                                    0xfffffff00708e7d4

                                    sptm_generate_rtlbi_param

                                    15

                                    0xfffffff0070730ac

                                    sptm_register_cpu

                                    16

                                    0xfffffff007075114

                                    sptm_register_type

                                    17

                                    0xfffffff00708ec94

                                    sptm_sign_user_pointer

                                    18

                                    0xfffffff00708ed54

                                    sptm_auth_user_pointer

                                    19

                                    0xfffffff00708a020

                                    sptm_register_xnu_exception_return

                                    20

                                    0xfffffff007073814

                                    sptm_cpu_unknown_func

                                    21

                                    0xfffffff007073530

                                    sptm_slide_region

                                    22

                                    0xfffffff00708d428

                                    sptm_update_papt

                                    txm

                                    函数地址

                                    函数名称

                                    1

                                    0xfffffff0070751cc

                                    2

                                    0xfffffff007089e60

                                    sptm_register_dispatch_table

                                    3

                                    0xfffffff00708a2ec

                                    sptm_retype

                                    4

                                    0xfffffff00708ee28

                                    Unknown

                                    函数地址

                                    函数名称

                                    1

                                    0xfffffff007089e60

                                    sptm_register_dispatch_table

                                    2

                                    0xfffffff00708a2ec

                                    sptm_retype

                                    3

                                    0xfffffff0070878f8

                                    dart

                                    函数地址

                                    函数名称

                                    1

                                    0xfffffff00707dee0

                                    2

                                    0xfffffff00707d498

                                    3

                                    0xfffffff00707cdec

                                    4

                                    0xfffffff00707c1bc

                                    5

                                    0xfffffff00707bdf4

                                    6

                                    0xfffffff00707aeb4

                                    7

                                    0xfffffff007079e0c

                                    8

                                    0xfffffff007079b54

                                    9

                                    0xfffffff0070798d8

                                    10

                                    0xfffffff00707962c

                                    11

                                    0xfffffff0070794f4

                                    12

                                    0xfffffff007079358

                                    13

                                    0xfffffff007079068

                                    14

                                    0xfffffff007078e8c

                                    15

                                    0xfffffff007078ca8

                                    16

                                    0xfffffff007078a60

                                    17

                                    0xfffffff007078044

                                    Unknown

                                    函数地址

                                    函数名称

                                    1

                                    0xfffffff00707e928

                                    2

                                    0xfffffff00707e800

                                    sart

                                    函数地址

                                    函数名称

                                    1

                                    0xfffffff00707791c

                                    2

                                    0xfffffff0070774dc

                                    3

                                    0xfffffff007077000

                                    nvme

                                    函数地址

                                    函数名称

                                    1

                                    0xfffffff007076078

                                    2

                                    0xfffffff0070759e4

                                    3

                                    0xfffffff0070756fc

                                    4

                                    0xfffffff0070754c0

                                    5

                                    0xfffffff007075000

                                    uat

                                    函数地址

                                    函数名称

                                    1

                                    0xfffffff0070718ec

                                    2

                                    0xfffffff0070716e8

                                    3

                                    0xfffffff00707135c

                                    4

                                    0xfffffff0070710c8

                                    5

                                    0xfffffff007070fdc

                                    6

                                    0xfffffff007070978

                                    7

                                    0xfffffff0070707c0

                                    8

                                    0xfffffff0070704d4

                                    9

                                    0xfffffff007070190

                                    10

                                    0xfffffff00706f628

                                    11

                                    0xfffffff00706f340

                                    12

                                    0xfffffff00706eb14

                                    13

                                    0xfffffff00706e97c

                                    1705915630067

                                    不同的switch case最后统一调用sptm_dispatch_entry_point函数:

                                    函数原型:sptm_dispatch_entry_point(uint64_t table_entry_point)

                                    1705925859826

                                        首先从gl2_thread中恢复sptm_dispatch需要的各种参数,这些参数在gxf_entry_el1开始处进行了保存。

                                    1705926019600

                                        然后就可以调用具体的entry_point了。

                                    3 Exception处理分析

                                        sptm在启动阶段,将syn_handler_sp0_g12地址设置为vbar_gl1的异常处理地址。

                                    1705977573086

                                        以syn_handler_sp0_g12+0x400的异常处理函数为例,追踪sptm如何处理来自gl0的异常,逻辑还原如下:

                                      syn_handler_low_gl2(uint64_t event_metadata)
                                      {
                                         uint64_t ec_value = (msr(ESR_GL1) >> 26) & 3f;
                                         uint64_t svc_number = msr(ESR_GL1) & 0xffff;
                                         if (ec_value == 0x15) {
                                           if (svc_number == 0) {
                                             uint64_t value = (event_metadata >> 32) & 0xffff;
                                             if (value != 0xfd) {
                                               if (value != 0xfe) {
                                                 if (value != 0xff) {
                                                   dispatch_state_machine(2, event_metadata);
                                                 }
                                                 else {
                                                   dispatch_state_machine(71);
                                                 }
                                               }
                                               else {
                                                 dispatch_state_machine(0xb0);
                                               }
                                              }
                                             else {
                                               dispatch_state_machine(50);
                                             }
                                           }
                                           else if (svc_number == 37) {
                                              uint64_t value = mrs(SPSR_GL1);
                                             msr(value & ~0x1C0);
                                           }
                                           else if (svc_number == 38) {
                                              uint64_t value = mrs(SPSR_GL1);
                                              msr(value | 0x1C0);
                                            }
                                           else {
                                             dead_loop();
                                           }
                                         }
                                         else if (ec_value == 0x16) {
                                         }
                                         else {
                                           handle_unkdown_exception_gl12();
                                         }
                                      }

                                          syn_handler_low_gl2提供了3个系统调用:

                                        Svc 0
                                        Svc 37
                                        Svc 38

                                            后两个系统调用用于关闭中断。

                                        Svc 0有如下触发路径:

                                          dispatch_state_machine(2, event_metadata);
                                          dispatch_state_machine(71);
                                          dispatch_state_machine(0xb, 0);
                                          dispatch_state_machine(50);

                                              同时可以看到,异常处理中并没有对关键寄存器进行保护。

                                          4 sptm页表保护

                                              sptmsecure page table monitor的缩写,ios将原本xnu内核中的页表映射代码转移到了sptm,也就是gl2模式进行处理。

                                              页表映射的函数原型:sptm_map_page(uint64_t paddr, uint64_t vaddr, uint64_t new_pte)

                                              首先调用acquire_root_pt获取root页目录表的地址。

                                          1705979708388

                                              接着调用check_vaddr_range检查虚拟地址vaddr是否在root页目标表所能翻译的地址范围内,注意每层目录表都有一个能翻译的地址范围。

                                          1705979782003(1)

                                              接着检查new_pte合法性。

                                          1705979898089(1)

                                              然后通过root_pt对应的attr_idx,在attr_struct_array中找到对应的结构体,判断物理地址paddr是否合法。

                                          1705979953206

                                              接着获取中间目录表和最后的页表结构体。

                                          1705980083112

                                              重点来了,sptm_type_params_database数组中隐藏着巨大秘密。

                                          1705981082105首先通过页表page_fte->typesptm_type_params_database数组中找到对应的struct current_type_params结构体,它保存着此种类型页表对应的sprr_index,然后在new_pte中提取出新的页表对应的sprr_index,将两者对比,如果不相等则中止此次页表映射。也就是说sptm维护了一组sprr_index数据库,只有类型匹配才能有权限进行映射。

                                          5 sptm retype分类保护

                                          Sptm将不同的数据段或代码段进行分类保护,每种不同的类型都有对应的地址范围,不同的类型不能互相映射,用分类概念完成了地址隔离保护。

                                              在sptm启动阶段,调用sptm_bootstrap_early对若干地址区域进行了分类注册:

                                          1705991597946

                                              对papt_range_array中的所有地址范围进行注册, retype类型为0x9。这些地址范围包括:SPTM-rxSPTM-rwSPTM-leTXM-roTXM-rxTXM-bxTXM-rwBootKC-roBootKC-rsBootKC-rxBootKC-bxBootKC-rwBootKC-leTrustCacheCL4-roCL4-rxCL4-rwCL4-leRAMDiskRTBuddySegSEPFWSEPPatchesuStuffpreoslogBootArgsExclaveOSIntegrityCatalogExclaveOSTrustCacheAVAILABLEPHYS_SLIDE等。

                                              还有其他的地址范围注册了以下retype类型:0xb0x130x36

                                            __text:FFFFFFF0070866A8 MOV             X0, #0xFFFFFFF000000000
                                            __text:FFFFFFF0070866AC MOV             W1, #1
                                            __text:FFFFFFF0070866B0 MOV             W2, #0
                                            __text:FFFFFFF0070866B4 MOV             W3, #0x13
                                            __text:FFFFFFF0070866B8 MOV             W4, #3
                                            __text:FFFFFFF0070866BC MOV             W5, #1
                                            __text:FFFFFFF0070866C0 BL              retype_handler
                                            retype_handler(0xFFFFFFF000000000,1,0,0x13,3,1)
                                            __text:FFFFFFF0070866D8 ADD             X22, X22, #4,LSL#12
                                            __text:FFFFFFF0070866DC MOV             X0, X22
                                            __text:FFFFFFF0070866E0 MOV             W1, #2
                                            __text:FFFFFFF0070866E4 MOV             W2, #0
                                            __text:FFFFFFF0070866E8 MOV             W3, #0x13
                                            __text:FFFFFFF0070866EC MOV             W4, #3
                                            __text:FFFFFFF0070866F0 MOV             W5, #1
                                            __text:FFFFFFF0070866F4 BL              retype_handler

                                            retype_handler(x22,2,0,0x13,3,1)

                                            retype_handler原型:

                                              retype_handler(uint64_t base, uint64_t length, x2, uint64_t frame_type, uint64_t max_num, bool flag);

                                              1705993076335

                                                  首先根据参数baseretype_range_array数组中找到其对应的retype_array数组地址。

                                                  接着调用bootstrap_alloc_frames分配一个retype帧结构体。

                                              1705993329299

                                              然后调用bootstrap_retype_frames将刚分配到的retype帧结构进行下一步操作。

                                              1705993438713

                                              bootstrap_retype_frames函数原型:

                                                bootstrap_retype_frames(uint64_t frame_start_address, uint64_t frame_num, uint64_t old_type, uint64_t new_type);

                                                    bootstrap_retype_frames调用set_retype_by_sprr完成类型编码的存储操作。

                                                    set_retype_by_sprr原型:

                                                  set_retype_by_sprr(uint64_t *retype_address, uint64_t sprr_index)

                                                  1705994270713

                                                      最终看到是将sprr_index编码进了retype_array数组。

                                                  6 txm

                                                  txmtrust execution monitor的缩写,运行在gl0,通过svc请求sptm服务:

                                                    __bootcode:FFFFFFF017054084 loc_FFFFFFF017054084 ; CODE XREF: sub_FFFFFFF017021C44+291BC↑j
                                                    __bootcode:FFFFFFF017054084 ; sub_FFFFFFF01704AE04+10↑j ...
                                                    __bootcode:FFFFFFF017054084 PACIBSP
                                                    __bootcode:FFFFFFF017054088 SVC             0
                                                    __bootcode:FFFFFFF01705408C RETAB
                                                    __bootcode:FFFFFFF01705408C ; END OF FUNCTION CHUNK FOR sub_FFFFFFF017021C44
                                                    __bootcode:FFFFFFF017054090 sub_FFFFFFF017054090 ; CODE XREF: start+48↑p
                                                    __bootcode:FFFFFFF017054090
                                                    __bootcode:FFFFFFF017054090 ; FUNCTION CHUNK AT __bootcode:FFFFFFF017054084 SIZE 0000000C BYTES
                                                    __bootcode:FFFFFFF017054090
                                                    __bootcode:FFFFFFF017054090 MOVK            X16, #0,LSL#48
                                                    __bootcode:FFFFFFF017054094 MOVK            X16, #1,LSL#32
                                                    __bootcode:FFFFFFF017054098 MOVK            X16, #0,LSL#16
                                                    __bootcode:FFFFFFF01705409C MOVK            X16, #1
                                                    __bootcode:FFFFFFF0170540A0 B               loc_FFFFFFF017054084

                                                    阅读原文

                                                    跳转微信打开