IOS 代码签名深度分析

· 2026-01-03 08:34 · 3 阅读

原创 wzt 2026-01-03 08:34 浙江

OS的代码签名机制是套复杂的系统工程,本文从用户态工具到内核态处理流程对ios代码签名机制做深度分析

1 简介

    IOS的代码签名机制是套复杂的系统工程,本文从用户态工具到内核态处理流程对ios代码签名机制做一个完全的分析。

2 用户态工具

  苹果公司的objectcswift语言经过编译后的二进制文件为mach-o格式,这是苹果公司自己设计的一个二进制文件格式,通过xcode的签名工具将签名信息保存在macho文件的一个特定sectionLC_CODE_SIGNATURE

  苹果公司对macho的签名包含两种信息,第一个信息是文件的hash,以页为单位算出hash值,将所有的hash值保存在上述section里。另一个信息是由苹果公司签发的证书信息。证书用来证明app的来源可靠,hash值用来检测每次app启动时的完整性,确保不被篡改。

2.1 签名信息的格式

    xcode调用codesign用户态工具,对app进行签名,签名信息被llvm编译器链接进macho里,它被表示成一个数据结构为:

osfmk/kern/cs_blobs.h

    typedef struct __CodeDirectory {
     uint32_t magic;                                 /* magic number (CSMAGIC_CODEDIRECTORY) */
     uint32_t length;                                /* total length of CodeDirectory blob */
     uint32_t version;                               /* compatibility version */
     uint32_t flags;                                 /* setup and mode flags */
     uint32_t hashOffset;                    /* offset of hash slot element at index zero */
     uint32_t identOffset;                   /* offset of identifier string */
     uint32_t nSpecialSlots;                 /* number of special hash slots */
     uint32_t nCodeSlots;                    /* number of ordinary (code) hash slots */
     uint32_t codeLimit;                             /* limit to main image signature range */
     uint8_t hashSize;                               /* size of each hash in bytes */
    ...
     uint8_t hashType;
    } CS_CodeDirectory
    __attribute__ ((aligned(1)));

        其中hashOffset保存的就是文件以页为单位的所有sha256 hashmacos系统没有自带的工具能识别出这些hash,可以使用开源的jtool2工具,本文使用笔者自己开发的一个工具ikit工具来解析签名信息。

    1

      它可以分析ioskernelcache,解析device tree, 提取kext,解析macho文件的hash信息、entitlements以及cms信息。

      使用--sig参数提取safari的所有hash信息。

      ./ikit --sig -/Applications/Safari.app/Contents/MacOS/Safari >safari.hash

      1

          CodeDirectory除了保存文件的hash值以外,还会保存另外七个特殊类型的hash

        CSSLOT_CODEDIRECTORY = 0,                               /* slot index for CodeDirectory */
         CSSLOT_INFOSLOT = 1,
         CSSLOT_REQUIREMENTS = 2,
         CSSLOT_RESOURCEDIR = 3,
         CSSLOT_APPLICATION = 4,
         CSSLOT_ENTITLEMENTS = 5,
         CSSLOT_DER_ENTITLEMENTS = 7,

            CSSLOT_DER_ENTITLEMENTS是苹果公司在新版macos/ios中新加入的entitlements类型,用DER格式表示。

        1

            Entitlements是整个xml信息的hash值,使用ikit--ent参数可以提取出所有的entitlements列表。

          ./ikit --ent -/Applications/Safari.app/Contents/MacOS/Safari >safari.ent

          1

          3 内核态流程

          3.1 简介

              IOS的代码签名过程分为启动前、运行时、退出时。

          3.1.1 进程启动前验签过程

          签名的解析发生在macho从磁盘加载到内核的过程中。

            bsd/kern/mach_loader.c
            parse_machfile(
             struct vnode            *vp,
             vm_map_t                map,
             thread_t                thread,
             struct mach_header      *header,
             off_t                   file_offset,
             off_t                   macho_size,
             int                     depth,
             int64_t                 aslr_offset,
             int64_t                 dyld_aslr_offset,
             load_result_t           *result,
             load_result_t           *binresult,
             struct image_params     *imgp
             )
            {
             if (os_add_overflow(mach_header_sz, header->sizeofcmds, &cmds_size) ||
                (off_t)cmds_size > macho_size ||
                round_page_overflow(cmds_size, &alloc_size) ||
                alloc_size > INT_MAX) {
             return LOAD_BADMACHO;
             }
             addr = kalloc_data(alloc_size, Z_WAITOK);
             if (addr == NULL) {
             return LOAD_NOSPACE;
             }
             error = vn_rdwr(UIO_READ, vp, addr, (int)alloc_size, file_offset,
                UIO_SYSSPACE, 0vfs_context_ucred(imgp->ip_vfs_context), &resid, p);
             if (error) {
             kfree_data(addr, alloc_size);
             return LOAD_IOERROR;
             }

                请注意xnu一次性将command全部从磁盘读入内存, text段等都包含在其内,这为后面校验hash提供了方便。

              ...
              case LC_CODE_SIGNATURE:
               ret = load_code_signature(
               (struct linkedit_data_command *) lcp,
               vp,
               file_offset,
               macho_size,
               header->cputype,
               header->cpusubtype,
               result,
               imgp);
               while (off < alloc_size && ret == LOAD_SUCCESS) {
               tainted = CS_VALIDATE_TAINTED;
               valid = cs_validate_range(vp,
               NULL,
              ile_offset + off,
               (const void *)((uintptr_t)addr + off),
              MIN(PAGE_SIZE, cmds_size),
              &tainted);
              }
              }

              load_code_signature用来解析签名信息。

                staticload_return_t
                load_code_signature(
                 struct linkedit_data_command    *lcp,
                 struct vnode                    *vp,
                 off_t                           macho_offset,
                 off_t                           macho_size,
                 cpu_type_t                      cputype,
                 cpu_subtype_t                   cpusubtype,
                 load_result_t                   *result,
                 struct image_params             *imgp)
                {
                 blob = ubc_cs_blob_get(vp, cputype, cpusubtype, macho_offset);
                 if (blob != NULL) {
                return;
                 }

                    首先从文件vnode节点中提取出一个struct cs_blob结构,它保存的是签名信息,如果这个签名信息已经存在,则直接返回成功。

                  kr = ubc_cs_blob_allocate(&addr, &blob_size);
                  if (kr != KERN_SUCCESS) {
                  ret = LOAD_NOSPACE;
                  goto out;
                   }
                  resid = 0;
                  error = vn_rdwr(UIO_READ,
                  vp,
                  (caddr_t) addr,
                  lcp->datasize,
                  macho_offset + lcp->dataoff,
                  UIO_SYSSPACE,
                  0,
                  kauth_cred_get(),
                  &resid,
                  current_proc());

                    如果不存在则调用ubc_cs_blob_allocate分配一块内存,调用vn_rdwr把签名信息读取到新分配的内存里。

                    if (ubc_cs_blob_add(vp,
                     result->ip_platform,
                     cputype,
                     cpusubtype,
                     macho_offset,
                     &addr,
                     lcp->datasize,
                     imgp,
                     0,
                     &blob))

                        调用ubc_cs_blob_addcsblob加入到链表中。

                      int
                      ubc_cs_blob_add(
                       struct vnode    *vp,
                       uint32_t        platform,
                       cpu_type_t      cputype,
                       cpu_subtype_t   cpusubtype,
                       off_t           base_offset,
                       vm_address_t    *addr,
                       vm_size_t       size,
                       struct image_params *imgp,
                       __unused int    flags,
                       struct cs_blob  **ret_blob)
                      {
                      error = cs_blob_init_validated(addr, size, &tmp_blob, &cd);

                          首先验证签名信息的格式是否正确,这里的校验并不完全严格,请读者自行阅读。

                        blob_ro = zalloc_ro(ZONE_ID_CS_BLOBZ_WAITOK | Z_NOFAIL);
                        tmp_blob.csb_ro_addr = blob_ro;
                        tmp_blob.csb_vnode = vp;
                        /* AMFI needs to see the current blob state at the RO address. */
                        zalloc_ro_update_elem(ZONE_ID_CS_BLOB, blob_ro, &tmp_blob);

                            注意csblob结构体使用zlloc的接口将其信息通过PPL保存在只读内存区,请参考后面关于PPL的分析部分。

                          #if CONFIG_MACF
                           error = mac_vnode_check_signature(vp, &tmp_blob, imgp, &cs_flags, &signer_type, flags, platform);
                          #endif
                          }

                              mac_vnode_check_signature继续调用AMFI内核模块的接口,苹果公司的签名核心技术都存放于这个内核模块中,由于不会开源,我们将在下一章使用逆向过程技术还原出它所有的核心逻辑。

                            当load_code_signature返回成功后,则会调用cs_validate_range对所有的文件hash做对比测试,确保当前计算出的hash与文件加载进来的hash匹配。

                          注意:即使vnode中已经存在csblob信息,它只是不在走AMFI的流程,对所有文件的hash对比是每次都要进行的!因为保存在磁盘的时间,应用程序可能已经被窜改。

                          3.1.2 运行时验签过程

                              进程在启动时ios已经做了一次签名验证,但是在进程运行的过程中,代码和数据还是可能被篡改掉的,那么如何在进程的运行过程中进行签名的验证呢?它的验签逻辑可以发生在进程调度、系统调用过程、page fault甚至可以开一个单独的线程每隔一定时间进行轮询验签,但是作为一个工业级的os,需要考虑性能功耗的问题。ios选择了在page fault中做代码的hash值对比操作。

                              引起Page fault的原因有很多种,有发生在内核空间中的,有发生在用户空间中的,比如应用程序越界访问内核、应用程序对只读内存进行写操作,还有应用程序访问自身一段还没有被映射的页面,这个错误可以引发Copy on write动作或者这个页面之前被交换到磁盘上,那么在页面换回时,势必要再进行一次签名验证。

                          3.1.2.1 权限错误处理流程

                              在进行异常处理前,需要先判断一下引起这个fault的物理页是否需要做一次签名验证,因为如果这块物理页已经被污染过,那么起始没必要进行fault处理了。如果没被污染, ios也有一些优化可以快速避开本次验签过程。vm_fault_cs_check_violation函数就是用来做这些事情。

                            vm_fault_cs_check_violation(
                             bool cs_bypass,
                             vm_object_t object,
                             vm_page_t m,
                             pmap_t pmap,
                             vm_prot_t prot,
                             vm_prot_t caller_prot,
                             vm_map_size_t fault_page_size,
                             vm_map_offset_t fault_phys_offset,
                             vm_object_fault_info_t fault_info,
                             bool map_is_switched,
                             bool map_is_switch_protected,
                             bool *cs_violation)
                            {
                             if (!cs_bypass &&
                                 vm_fault_cs_need_validation(pmap, m, object,
                                 fault_page_size, fault_phys_offset)) {
                             vm_object_lock_assert_exclusive(object);
                             if (VMP_CS_VALIDATED(m, fault_page_size, fault_phys_offset)) {
                             vm_cs_revalidates++;
                             }
                             vm_page_validate_cs(m, fault_page_size, fault_phys_offset);
                            }
                            }

                                vm_fault_cs_need_validation用来判断这个物理页是否需要进行验签过程。

                              staticbool
                              vm_fault_cs_need_validation(
                               pmap_t pmap,
                               vm_page_t page,
                               vm_object_t page_obj,
                               vm_map_size_t fault_page_size,
                               vm_map_offset_t fault_phys_offset)
                              {
                               if (pmap == kernel_pmap) {
                               /* 1 - not user space */
                               return false;
                               }

                                  验签只针对用户态代码和数据。

                                 if (!page_obj->code_signed) {
                                 /* 3 - page does not belong to a code-signed object */
                                 return false;
                                 }
                                 if (fault_page_size == PAGE_SIZE) {
                                 /* looking at the whole page */
                                 if (page->vmp_cs_tainted == VMP_CS_ALL_TRUE) {
                                 /* 2 - page is all tainted */
                                 return false;
                                 }

                                    如果这个物理页已经被污染掉,此次就不在需要再判断了。

                                   if (page->vmp_cs_validated == VMP_CS_ALL_TRUE &&
                                       !page->vmp_wpmapped) {
                                   /* 4 - already fully validated and never mapped writable */
                                   return false;
                                   }

                                      如果这个物理页此前已经被处理过,那么此次fault也不会再判断了。Iosjit签名就是利用了这一点绕过了签名的验证。

                                    else {
                                     /* looking at a specific sub-page */
                                     if (VMP_CS_TAINTED(page, fault_page_size, fault_phys_offset)) {
                                     /* 2 - sub-page was already marked as tainted */
                                     return false;
                                     }
                                     if (VMP_CS_VALIDATED(page, fault_page_size, fault_phys_offset) &&
                                         !page->vmp_wpmapped) {
                                     /* 4 - already validated and never mapped writable */
                                     return false;
                                     }
                                     }
                                     /* page needs to be validated */
                                     return true;
                                    }
                                    void
                                    vm_page_validate_cs(
                                     vm_page_t       page,
                                     vm_map_size_t   fault_page_size,
                                     vm_map_offset_t fault_phys_offset)
                                    {
                                     vm_object_t             object;
                                     object = VM_PAGE_OBJECT(page);
                                     vm_object_lock_assert_held(object);
                                     if (vm_page_validate_cs_fast(page, fault_page_size, fault_phys_offset)) {
                                     return;
                                     }
                                     vm_page_map_and_validate_cs(object, page);
                                    }
                                    staticboolean_t
                                    vm_page_validate_cs_fast(
                                     vm_page_t       page,
                                     vm_map_size_t   fault_page_size,
                                     vm_map_offset_t fault_phys_offset)
                                    {
                                     vm_object_t     object;
                                     object = VM_PAGE_OBJECT(page);
                                     vm_object_lock_assert_held(object);
                                     if (page->vmp_wpmapped&&
                                         !VMP_CS_TAINTED(page, fault_page_size, fault_phys_offset)) {
                                     /*
                                      * This page was mapped for "write" access sometime in the
                                      * past and could still be modifiable in the future.
                                      * Consider it tainted.
                                      * [ If the page was already found to be "tainted", no
                                      * need to re-validate. ]
                                      */
                                     vm_object_lock_assert_exclusive(object);
                                     VMP_CS_SET_VALIDATED(page, fault_page_size, fault_phys_offset, TRUE);
                                     VMP_CS_SET_TAINTED(page, fault_page_size, fault_phys_offset, TRUE);
                                     if (cs_debug) {
                                     printf("CODESIGNING: %s: "
                                         "page %p obj %p off 0x%llx "
                                         "was modified\n",
                                         __FUNCTION__,
                                         page, object, page->vmp_offset);
                                     }
                                     vm_cs_validated_dirtied++;
                                     }
                                     if (VMP_CS_VALIDATED(page, fault_page_size, fault_phys_offset) ||
                                         VMP_CS_TAINTED(page, fault_page_size, fault_phys_offset)) {
                                     return TRUE;
                                     }
                                    }

                                        vm_page_validate_cs_fast有个特殊的处理,如果page->vmp_wpmapped被设置,说明这个物理页在之前的某段时间里被设置为可写状态,xnu认为在将来的一段时间这个可写状态会一直保持,所以直接将这个物理页标记为污染和审计过,直接返回。

                                        vm_page_map_and_validate_cs函数则对这个物理页做hash对比操作,读者可自行阅读相关代码。

                                    3.1.2.2 磁盘交换处理流程

                                        接下来就要分析页面从磁盘交换回内存时的签名验证过程:

                                      Osfmk/vm/vm_fault.c
                                      vm_fault_page->memory_object_data_request
                                      Osfmk/vm/memory_object.c
                                      kern_return_t
                                      memory_object_data_request
                                      (
                                       memory_object_t memory_object,
                                       memory_object_offset_t offset,
                                       memory_object_cluster_size_t length,
                                       vm_prot_t desired_access,
                                       memory_object_fault_info_t fault_info
                                      )
                                      {
                                       return (memory_object->mo_pager_ops->memory_object_data_request)(
                                       memory_object,
                                       offset,
                                       length,
                                       desired_access,
                                       fault_info);
                                      }

                                          xnu提供了几种不同的内存后备存储方式,比如swap交换文件、普通文件、内存压缩、 apple特有的内存加密方式,这些后备存储都对应不同的分页器,但只有几种后备存储才会在内存交换回来时做签名验证这里我们只以4k形式的分页器进行分析。

                                        Osfmk/vm/vm_fourk_pager.c
                                        const struct memory_object_pager_ops fourk_pager_ops = {
                                         .memory_object_reference = fourk_pager_reference,
                                         .memory_object_deallocate = fourk_pager_deallocate,
                                         .memory_object_init = fourk_pager_init,
                                         .memory_object_terminate = fourk_pager_terminate,
                                         .memory_object_data_request = fourk_pager_data_request,
                                         .memory_object_data_return = fourk_pager_data_return,
                                         .memory_object_data_initialize = fourk_pager_data_initialize,
                                         .memory_object_data_unlock = fourk_pager_data_unlock,
                                         .memory_object_synchronize = fourk_pager_synchronize,
                                         .memory_object_map = fourk_pager_map,
                                         .memory_object_last_unmap = fourk_pager_last_unmap,
                                         .memory_object_data_reclaim = NULL,
                                         .memory_object_backing_object = NULL,
                                         .memory_object_pager_name = "fourk_pager"
                                        };
                                        fourk_pager_data_request(
                                         memory_object_t         mem_obj,
                                         memory_object_offset_t  offset,
                                         memory_object_cluster_size_t            length,
                                         vm_prot_t               protection_required,
                                         memory_object_fault_info_t mo_fault_info)
                                        {
                                         subpg_validated = FALSE;
                                         subpg_tainted = 0;
                                         if (src_page_object->code_signed) {
                                                  vm_page_validate_cs_mapped_chunk(
                                         src_page,
                                         (const void *) src_vaddr,
                                         offset_in_src_page,
                                         FOURK_PAGE_SIZE,
                                         &subpg_validated,
                                         &subpg_tainted);
                                         num_subpg_signed++;
                                         if (subpg_validated) {
                                         num_subpg_validated++;
                                         }
                                         if (subpg_tainted & CS_VALIDATE_TAINTED) {
                                         num_subpg_tainted++;
                                         }
                                         if (subpg_tainted & CS_VALIDATE_NX) {
                                         /* subpg should not be executable */
                                         if (sub_page_cnt > 1) {
                                         } else {
                                         num_subpg_nx++;
                                         }
                                         }
                                         }

                                            vm_page_validate_cs_mapped_chunk对一块物理内存进行签名验证,因为在页面被交换到磁盘的这段时间内,可能会被篡改掉。

                                        3.1.2.3 页面复制时

                                            此外在进行一个物理页面的复制时,也需要对源物理页进行签名验证。

                                          osfmk\vm\vm_resident.c
                                          void
                                          vm_page_copy(
                                           vm_page_t       src_m,
                                           vm_page_t       dest_m)
                                          {
                                           vm_object_t     src_m_object;
                                           src_m_object = VM_PAGE_OBJECT(src_m);
                                           vm_object_lock_assert_held(src_m_object);
                                           if (src_m_object != VM_OBJECT_NULL &&
                                               src_m_object->code_signed) {
                                           vm_page_copy_cs_validations++;
                                           vm_page_validate_cs(src_m, PAGE_SIZE, 0);
                                           }
                                           dest_m->vmp_cs_tainted = src_m->vmp_cs_tainted;
                                           dest_m->vmp_cs_nx = src_m->vmp_cs_nx;
                                           if (dest_m->vmp_cs_tainted) {
                                           vm_page_copy_cs_tainted++;
                                           }
                                           dest_m->vmp_error = src_m->vmp_error; /* sliding src_m might have failed... */
                                           pmap_copy_page(VM_PAGE_GET_PHYS_PAGE(src_m), VM_PAGE_GET_PHYS_PAGE(dest_m));
                                          }

                                              Src_m代表源页面,如果它对应的object对象有code_signed标志,那么就会进行一次签名验证,页面拷贝需要确保它的完整性。

                                          3.1.2.4 只读rootfs的优化

                                              对于只读rootfs上的文件,xnu做了优化,在page fault第一次验证完签名后,如果当前进程没有试图将只读rootfs挂载为可写,那么后续就不再做签名验证,属于性能优化的一部分。

                                          Bsd/sys/ubc_internal.h

                                            struct ubc_info {
                                             struct  cs_blob         * cs_blobs; /* for CODE SIGNING */
                                            #if CONFIG_SUPPLEMENTAL_SIGNATURES
                                             struct  cs_blob         * cs_blob_supplement;/* supplemental blob (note that there can only be one supplement) */
                                            #endif
                                            #if CHECK_CS_VALIDATION_BITMAP
                                             void                    * XNU_PTRAUTH_SIGNED_PTR("ubc_info.cs_valid_bitmap") cs_valid_bitmap;     /* right now: used only for signed files on the read-only root volume */
                                             uint64_t                cs_valid_bitmap_size;
                                            }

                                                Ubc_info结构体除了保存struct cs_blob结构体外,还可选的保存了一个bitmap,它用来设置一个物理页的签名状态。

                                            Osfmk/vm/vm_protos.h

                                            #define CS_BITMAP_SET1//对页面设置为已经检查过

                                            #define CS_BITMAP_CLEAR 2// 清除签名状态

                                            #define CS_BITMAP_CHECK 3// 对页面进行签名检查

                                            vm_page_validate_cs_fast函数前面讲过它会进行预判此次page fault是否需要验证这个页面的签名。

                                              staticboolean_t
                                              vm_page_validate_cs_fast(
                                               vm_page_t       page,
                                               vm_map_size_t   fault_page_size,
                                               vm_map_offset_t fault_phys_offset)
                                              {
                                              #if CHECK_CS_VALIDATION_BITMAP
                                               kern_return_t kr;
                                               kr = vnode_pager_cs_check_validation_bitmap(
                                               object->pager,
                                               page->vmp_offset + object->paging_offset,
                                               CS_BITMAP_CHECK);
                                               if (kr == KERN_SUCCESS) {
                                               page->vmp_cs_validated = VMP_CS_ALL_TRUE;
                                               page->vmp_cs_tainted = VMP_CS_ALL_FALSE;
                                               vm_cs_bitmap_validated++;
                                               return TRUE;
                                               }
                                              #endif

                                                它会调用vnode_pager_cs_check_validation_bitmap传递CS_BITMAP_CHECK进行判断是否需要签名验证。

                                              vnode_pager_cs_check_validation_bitmap进一步调用ubc_cs_check_validation_bitmap

                                                kern_return_t
                                                ubc_cs_check_validation_bitmap(
                                                 vnode_t                 vp,
                                                 memory_object_offset_t          offset,
                                                 int                     optype)
                                                {
                                                 kern_return_t   kr = KERN_SUCCESS;
                                                 if (!USE_CODE_SIGN_BITMAP(vp)|| !UBCINFOEXISTS(vp)) {              【1
                                                 kr = KERN_INVALID_ARGUMENT;
                                                 } else {
                                                 struct ubc_info *uip = vp->v_ubcinfo;
                                                 char            *target_bitmap = uip->cs_valid_bitmap;
                                                 if (target_bitmap == NULL) {
                                                 kr = KERN_INVALID_ARGUMENT;
                                                 } else {
                                                 uint64_t        bit, byte;
                                                 bit = atop_64( offset );
                                                 byte = bit >> 3;
                                                 if (byte > uip->cs_valid_bitmap_size) {
                                                 kr = KERN_INVALID_ARGUMENT;
                                                 } else {
                                                 if (optype == CS_BITMAP_SET) {
                                                 target_bitmap[byte] |= (1 << (bit & 07));
                                                 kr = KERN_SUCCESS;
                                                 } else if (optype == CS_BITMAP_CLEAR) {
                                                 target_bitmap[byte] &= ~(1 << (bit & 07));
                                                 kr = KERN_SUCCESS;
                                                 } else if (optype == CS_BITMAP_CHECK) {
                                                 if (target_bitmap[byte] & (1 << (bit & 07))) {           【2
                                                 kr = KERN_SUCCESS;
                                                 } else {
                                                 kr = KERN_FAILURE;
                                                 }
                                                 }
                                                 }
                                                 }
                                                 }
                                                 return kr;
                                                }

                                                  #define USE_CODE_SIGN_BITMAP(vp) ( (vp != NULL&& (vp->v_mount != NULL&& (vp->v_mount->mnt_flag & MNT_ROOTFS) && !root_fs_upgrade_try)

                                                      USE_CODE_SIGN_BITMAP这个首先宏判断vnode是不是rootfs,并且要判断root_fs_upgrade_try是否为0。这个变量代表的是进程试图将只读的文件系统挂载为可写。

                                                    boolean_t root_fs_upgrade_try = FALSE;
                                                    int
                                                    __mac_mount(struct proc *p, register struct __mac_mount_args *uap, __unused int32_t *retval)
                                                    {
                                                    #if CHECK_CS_VALIDATION_BITMAP
                                                     if ((flags & MNT_RDONLY) == 0) {
                                                     root_fs_upgrade_try = TRUE;
                                                     }
                                                    #endif
                                                    }

                                                      所以【1】处的逻辑为:如果vnode属于rootfs,并且root_fs_upgrade_try设置为了1,说明有进程将只读rootfs重新挂载为可写,那么就必须对这个物理页进行签名验证,否则在【2】处要进行一次页面曾经是否被检测过的判断,如果对应的比特位已经被设置,则无需进行签名验证,否则需要进行签名验证。我们看到CS_BITMAP_CHECK是用来判断比特位是否设置的,那么CS_BITMAP_SET是在什么时候调用的呢?答案在vm_page_validate_cs_mapped_slow中。

                                                      void
                                                      vm_page_validate_cs_mapped_slow(
                                                       vm_page_t       page,
                                                       const void      *kaddr)
                                                      {
                                                       vm_object_t             object;
                                                       memory_object_offset_t  mo_offset;
                                                       memory_object_t         pager;
                                                       struct vnode            *vnode;
                                                       int                     validated, tainted, nx;
                                                       assert(page->vmp_busy);
                                                       object = VM_PAGE_OBJECT(page);
                                                       vm_object_lock_assert_exclusive(object);
                                                       vm_cs_validates++;
                                                       pager = object->pager;
                                                       assert(object->paging_in_progress);
                                                       vnode = vnode_pager_lookup_vnode(pager);
                                                       mo_offset = page->vmp_offset + object->paging_offset;
                                                       /* verify the SHA1 hash for this page */
                                                       validated = 0;
                                                       tainted = 0;
                                                       nx = 0;
                                                       cs_validate_page(vnode,
                                                           pager,
                                                           mo_offset,
                                                           (const void *)((const char *)kaddr),
                                                           &validated,
                                                           &tainted,
                                                           &nx);
                                                       page->vmp_cs_validated |= validated;
                                                       page->vmp_cs_tainted |= tainted;
                                                       page->vmp_cs_nx |= nx;
                                                      #if CHECK_CS_VALIDATION_BITMAP
                                                       if (page->vmp_cs_validated == VMP_CS_ALL_TRUE &&
                                                           page->vmp_cs_tainted == VMP_CS_ALL_FALSE) {
                                                       vnode_pager_cs_check_validation_bitmap(object->pager,
                                                           mo_offset,
                                                           CS_BITMAP_SET);
                                                       }
                                                      #endif /* CHECK_CS_VALIDATION_BITMAP */
                                                      }

                                                          cs_validate_page对物理页进行hash值验证,如果合法,则调用vnode_pager_cs_check_validation_bitmap传递CS_BITMAP_SET对相应比特位进行了设置。此后ubc_cs_check_validation_bitmap发现如果页面比特位已经被设置,后面就无需再进行签名验证了。

                                                      3.1.3 进程退出时签名验证

                                                          在进程正常退出时,xnu是不会做签名验证的,只有在异常退出时,且有以下几种情况就会必须验证签名:当进程收到coredumpcrash report信号时,或者因进程签名验证失败收到CS_KILLED信号时。

                                                      Bsd/kern/kern_exit.c

                                                        void
                                                        proc_prepareexit(proc_t p, int rv, boolean_t perf_notify)
                                                        {
                                                         /*
                                                          * Generate a corefile/crashlog if:
                                                          *     The process doesn't have an exit reason that indicates no crash report should be created
                                                          *     AND any of the following are true:
                                                          *     - The process was terminated due to a fatal signal that generates a core
                                                          *     - The process was killed due to a code signing violation
                                                          *     - The process has an exit reason that indicates we should generate a crash report
                                                          *
                                                          * The first condition is necessary because abort_with_reason()/payload() use SIGABRT
                                                          * (which normally triggers a core) but may indicate that no crash report should be created.
                                                          */
                                                         if (!(PROC_HAS_EXITREASON(p) && (PROC_EXITREASON_FLAGS(p) & OS_REASON_FLAG_NO_CRASH_REPORT)) &&
                                                             (hassigprop(WTERMSIG(rv), SA_CORE) || ((proc_getcsflags(p) & CS_KILLED) != 0) ||
                                                             (PROC_HAS_EXITREASON(p) && (PROC_EXITREASON_FLAGS(p) &
                                                             OS_REASON_FLAG_GENERATE_CRASH_REPORT)))) {
                                                         task_t task = proc_task(p);
                                                         uintptr_t bt[2];
                                                         struct backtrace_user_info btinfo = BTUINFO_INIT;
                                                         unsigned int frame_count = backtrace_user(bt, 2NULL, &btinfo);            [1]
                                                         int bt_err = btinfo.btui_error;
                                                         if (bt_err == 0 && frame_count >= 1) {
                                                         /*
                                                          * First check at the page containing the current PC.
                                                          * This passes if the page code signs -or- if we can't figure out
                                                          * what is at that address. The latter action is so we continue checking
                                                          * previous pages which may be corrupt and caused a wild branch.
                                                          */
                                                         kr = revalidate_text_page(task,bt[0]);                               [2]
                                                         /* No corruption found, check the previous sequential page */
                                                         if (kr == KERN_SUCCESS) {
                                                         kr = revalidate_text_page(task,bt[0] - get_task_page_size(task));     [3]
                                                         }
                                                         /* Still no corruption found, check the current function's caller */
                                                         if (kr == KERN_SUCCESS) {
                                                         if (frame_count > 1 &&
                                                             atop(bt[0]) != atop(bt[1]) &&           /* don't recheck PC page */
                                                             atop(bt[0]) - 1 != atop(bt[1])) {       /* don't recheck page before */
                                                         kr = revalidate_text_page(task, (vm_map_offset_t)bt[1]);       [4]
                                                         }
                                                         }
                                                         if (kr != KERN_SUCCESS) {
                                                         os_log(OS_LOG_DEFAULT,
                                                             "Text page corruption detected in dying process %d\n"proc_getpid(p));
                                                         }
                                                         }
                                                         }

                                                            在[1]处先获取当前函数栈的backtrace信息,bt[0]代表当前的PC指针,bt[1]代表当前函数caller的地址。在[2]处调用revalidate_text_page对当前pc所在的物理页进行一次签名检查,如果没有问题,则在[3]处对当前pc地址所在的前一个物理页进行签名验证,没有还是没有问题,就在对函数的caller地址所在的物理页进行签名检测。我们看到这一系列操作只是试图检测当前pc附近的代码段有没有被破坏。

                                                          Osfmk/vm/vm_fault.c
                                                          kern_return_t
                                                          revalidate_text_page(task_t task, vm_map_offset_t code_addr)
                                                          {
                                                           kern_return_t          kr;
                                                           vm_map_t               map;
                                                           vm_object_t            object = NULL;
                                                           vm_object_offset_t     offset;
                                                           vm_page_t              page = NULL;
                                                           struct vnode           *vnode;
                                                           uint64_t               *diagnose_buffer = NULL;
                                                           CA_EVENT_TYPE(vmtc_telemetry) * event = NULL;
                                                           ca_event_t ca_event = NULL;
                                                           map = task->map;
                                                           if (task->map == NULL) {
                                                           return KERN_SUCCESS;
                                                           }
                                                           kr = vmtc_revalidate_lookup(map, code_addr, &object, &offset, &page);              [1]
                                                           if (kr != KERN_SUCCESS) {
                                                           goto done;
                                                           }
                                                           /*
                                                            * The object needs to have a pager.
                                                            */
                                                           if (object->pager == NULL) {
                                                           goto done;
                                                           }
                                                           /*
                                                            * Needs to be a vnode backed page to have a signature.
                                                            */
                                                           vnode = vnode_pager_lookup_vnode(object->pager);
                                                           if (vnode == NULL) {
                                                           goto done;
                                                           }
                                                           /*
                                                            * Object checks to see if we should proceed.
                                                            */
                                                           if (!object->code_signed ||     /* no code signature to check */
                                                               object->internal ||         /* internal objects aren't signed */
                                                               object->terminating ||      /* the object and its pages are already going away */
                                                               !object->pager_ready) {     /* this should happen, but check shouldn't hurt */
                                                           goto done;
                                                           }
                                                           /*
                                                            * Check the code signature of the page in question.
                                                            */
                                                           vm_page_map_and_validate_cs(object, page);                              [2]
                                                           /*
                                                            * At this point:
                                                            * vmp_cs_validated |= validated (set if a code signature exists)
                                                            * vmp_cs_tainted |= tainted (set if code signature violation)
                                                            * vmp_cs_nx |= nx;  ??
                                                            *
                                                            * if vmp_pmapped then have to pmap_disconnect..
                                                            * other flags to check on object or page?
                                                            */
                                                           if (page->vmp_cs_tainted != VMP_CS_ALL_FALSE) {                            [3]
                                                          #if DEBUG || DEVELOPMENT
                                                           /*
                                                            * On development builds, a boot-arg can be used to cause
                                                            * a panic, instead of a quiet repair.
                                                            */
                                                           if (vmtc_panic_instead) {
                                                           panic("Text page corruption detected: vm_page_t 0x%llx", (long long)(uintptr_t)page);
                                                           }
                                                          #endif/* DEBUG || DEVELOPMENT */
                                                           /*
                                                            * We're going to invalidate this page. Grab a copy of it for comparison.
                                                            */
                                                           ca_event = CA_EVENT_ALLOCATE(vmtc_telemetry);
                                                           event = ca_event->data;
                                                           diagnose_buffer = vmtc_text_page_diagnose_setup(code_addr, page, event);
                                                           /*
                                                            * Invalidate, i.e. toss, the corrupted page.
                                                            */
                                                           if (!page->vmp_cleaning &&
                                                               !page->vmp_laundry &&
                                                               !page->vmp_fictitious &&
                                                               !page->vmp_precious &&
                                                               !page->vmp_absent &&
                                                               !page->vmp_error &&
                                                               !page->vmp_dirty &&
                                                               !is_page_wired(page)) {
                                                           if (page->vmp_pmapped) {
                                                           int refmod = pmap_disconnect(VM_PAGE_GET_PHYS_PAGE(page));
                                                           if (refmod & VM_MEM_MODIFIED) {
                                                           SET_PAGE_DIRTY(page, FALSE);
                                                           }
                                                           if (refmod & VM_MEM_REFERENCED) {
                                                           page->vmp_reference = TRUE;
                                                           }
                                                           }
                                                           /* If the page seems intentionally modified, don't trash it. */
                                                           if (!page->vmp_dirty) {
                                                           VM_PAGE_FREE(page);
                                                           } else {
                                                           event->vmtc_not_eligible = true;
                                                           }
                                                           } else {
                                                           event->vmtc_not_eligible = true;
                                                           }
                                                           vm_object_unlock(object);
                                                           object = VM_OBJECT_NULL;
                                                           /*
                                                            * Now try to diagnose the type of failure by faulting
                                                            * in a new copy and diff'ing it with what we saved.
                                                            */
                                                           if (diagnose_buffer != NULL) {
                                                           vmtc_text_page_diagnose(code_addr, diagnose_buffer, event);
                                                           }
                                                          #if DEBUG || DEVELOPMENT
                                                           if (corruption_test_va != 0) {
                                                           corruption_test_va = 0;
                                                           event->vmtc_testing = true;
                                                           }
                                                          #endif/* DEBUG || DEVELOPMENT */
                                                                    kernel_triage_record(thread_tid(current_thread()),
                                                               KDBG_TRIAGE_EVENTID(KDBG_TRIAGE_SUBSYS_VM, KDBG_TRIAGE_RESERVED, KDBG_TRIAGE_VM_TEXT_CORRUPTION),
                                                               0 /* arg */);
                                                           CA_EVENT_SEND(ca_event);
                                                           printf("Text page corruption detected for pid %d\n"proc_selfpid());
                                                           ++vmtc_total;
                                                           return KERN_FAILURE;
                                                           }
                                                          done:
                                                           if (object != NULL) {
                                                           vm_object_unlock(object);
                                                           }
                                                           return KERN_SUCCESS;
                                                          }

                                                            在[1]处获取addr对应的objectpage信息,在[2]处调用vm_page_map_and_validate_cs来对这个page进行签名验证,在[3]处如果检测到签名失败,还要进行详细的信息获取,比如要对比这个page是哪一块内存出现了问题。

                                                          3.2 AMFI处理流程

                                                              Amfi(Apple Mobile File Integrity)是一个内核扩展, IOS的代码验签的过程都集中在这个模块中,本文以ios14为例来剖析ios的代码验签过程。

                                                          3.2.1 vnode_check_signature

                                                              MAC系统通过以下代码进入到amfi流程中:

                                                            int
                                                            mac_vnode_check_signature(struct vnode *vp, struct cs_blob *cs_blob,
                                                            struct image_params *imgp,
                                                            unsigned int *cs_flags, unsigned int *signer_type,
                                                            int flags, unsigned int platform) {
                                                             MAC_CHECK(vnode_check_signature, vp, vp->v_label, cpu_type, cs_blob,
                                                                 cs_flags, signer_type, flags, platform, &fatal_failure_desc, &fatal_failure_desc_len);
                                                            }

                                                              __ZL22_vnode_check_signatureP5vnodeP5labeliP7cs_blobPjS5_ijPPcPm
                                                              MOV W0, #1048 ; size
                                                              BL _IOMalloc_external
                                                              CBZ X0, loc_FFFFFFF008308F44
                                                              MOV X22, X0 ; unkown_struct;
                                                              LDP X25, X26, [X29,#arg_0]
                                                              ADD X0, X0, #8 ; __dst
                                                              MOV X27, X22
                                                              MOVK X27, #0x6712,LSL#48
                                                              ADRL X8, off_FFFFFFF0077AE4F0
                                                              PACDA X8, X27
                                                              STR X8, [X22] ; *(unkown_struct + 0) = pacda()
                                                              ADRL X1, l_.str.82 ; __src
                                                              MOV W2, #1024 ; __n
                                                              BL _memmove ; memmove(unkown_struct + 8, src, 1024);
                                                              STRB WZR, [X22,#1032] ; *(unkown_struct + 1032) = 0
                                                              ADRL X8, off_FFFFFFF0077AE4C0
                                                              PACDA X8, X27
                                                              STR X8, [X22]
                                                              STR X24, [X22,#1040] ; *(unkown_struct + 1040) = vnode
                                                              MOV X0, X22
                                                              MOV X1, X23
                                                              MOV X2, X21
                                                              MOV X3, X20
                                                              MOV X4, X19
                                                              MOV W5, #0
                                                              MOV X6, X25
                                                              MOV X7, X26 ; (unkown_struct, csblob, cs_flags, signer_type, flags, 0, *addr1, *addr2)
                                                              BL__ZN20StaticPlatformPolicyILb1ELb1ELb0ELb0ELb0ELb0ELb1ELb1ELb0ELb1ELb0ELb0ELb0ELb0ELb1ELb1ELj2ELb1ELb0EE15check_signatureEP8LazyPathiP7cs_blobPjS5_ibbPPcPm

                                                                  进一步调用check_signature函数,从上面的代码中,我们可以推测它的参数结构为:

                                                                check_signature(unkown_struct, csblob, cs_flags, signer_type, flags, 0, *addr1, *addr2)。

                                                                3.2.2 check_signature

                                                                3.2.2.1 cd hashtrustcache

                                                                  // (unkown_struct, csblob, cs_flags, signer_type, flags, 0, *addr1, *addr2)
                                                                  __int64 __fastcall StaticPlatformPolicy<true,true,false,false,false,false,true,true,false,true,false,false,false,false,true,true,2u,true,false>::check_signature(
                                                                  LazyPath *a1,
                                                                  __int64 a2,
                                                                  unsigned int *a3,
                                                                  _DWORD *a4,
                                                                  unsigned int a5,
                                                                  __int64 a6,
                                                                  char **a7,
                                                                  unsigned __int64 *a8)
                                                                  {
                                                                   if ( (StaticPlatformPolicy<true,true,false,false,false,false,true,true,false,true,false,false,false,false,true,true,2u,true,false>::loadEntitlementsFromSignature(
                                                                  &inColl,
                                                                  a2,
                                                                  &v114) & 1) == 0 )
                                                                   {
                                                                  v30 = fatal_error_fmt(
                                                                  a1,
                                                                  a7,
                                                                  a8,
                                                                  "The signature could not be validated because AMFI could not load its entitlements for validation: %s",
                                                                  v114);
                                                                  v31 = 0LL;
                                                                  v32 = 1LL;
                                                                  v16 = inColl;
                                                                  if ( !inColl )
                                                                  goto LABEL_20;
                                                                  goto LABEL_19;
                                                                   }

                                                                      loadEntitlementsFromSignature函数的作用是从struct cs_blob中获取进程的entitlements,它的大小在8byte128m之间。如果cs_blob存储的cd hashstatic trustcacheloaded trustcache中,那么会在entitlements中加入一个新节点platform-application如果cs_blob存取的identifier为空,则在entitlements中删除节点com.apple.private.signing-identifier,否则设置为cs_blob对应的identifier

                                                                    ...
                                                                    if ( v23 && (v24 = (const char *)((__int64 (*)(void))v23->__vftable[1].isEqualTo)()) != 0LL )
                                                                    {
                                                                    v25 = (OSDictionary *)v24;
                                                                    *a3 |= 0x200u;                     // cs_flags |= CS_KILL
                                                                    if ( !strcmp(v24, "get-task-allow") )
                                                                    {
                                                                     LOBYTE(v126[0]) = 0;
                                                                     if ( !(unsigned int)AppleMobileFileIntegrity::AMFIEntitlementGetBool(
                                                                    (AppleMobileFileIntegrity *)v16,
                                                                     v25,
                                                                     (const char *)v126,
                                                                     v26)
                                                                     && LOBYTE(v126[0]) != 0 )
                                                                     {
                                                                     *a3 |= 4u;                      // CS_GET_TASK_ALLOW
                                                                     }
                                                                    }
                                                                    }
                                                                    ...

                                                                        获取entitlements后,首先将第三个参数cs_flags加上CS_KILL标志。如果entitlements包含get-task-allow节点,则再把CS_GET_TASK_ALLOW标志加入到cs_flags中。

                                                                       cdhash = (const unsigned __int8 *)csblob_get_cdhash(v13);
                                                                       if ( !cdhash )
                                                                       {
                                                                       v30 = fatal_error_fmt(a1, a7, a8, "Internal Error: No cdhash found.");
                                                                       goto LABEL_37;
                                                                       }

                                                                          接着获取进程的cd hash,如果不存在直接返回。

                                                                        if ( (AMFIIsCodeDirectoryInTrustCache(cdhash) & 1) == 0 && !(unsigned int)codeDirectoryHashIsInLoadedTrustCache(v37) )
                                                                         {
                                                                        }

                                                                            如果cd hashstatic trustcacheloaded trustcache里。

                                                                           v39 = postValidation(a1, v13, v38, "in-kernel", a7, a8);
                                                                           v30 = IsCDHashBlacklisted(v37);
                                                                           if ( (_DWORD)v30 )
                                                                           {
                                                                          v30 = fatal_error_fmt(a1, a7, a8, "Bad things happened.");
                                                                          LABEL_37:
                                                                          v31 = 0LL;
                                                                          goto LABEL_82;
                                                                           }

                                                                              postValidation函数判断cs_flags是否包含CS_PLATFORM_BINARY,如果是返回1。否则继续判断hash type是否为sha256,如果是返回1,其余返回0

                                                                              Ios还为cd hash设置了一个黑名单,IsCDHashBlacklisted函数判断一个cd hash是否在黑名单中。

                                                                          *a3 |= 0x20000000u;// CS_SIGNED

                                                                              最后将CS_SIGNED标志放入到cs_flags中,函数返回。

                                                                              我们看到如果一个cd hashstatic trustcache或者在loaded trustcache里,则直接完成此次判断,接下来继续分析不在trustcache里的情况。

                                                                          3.2.2.2 cd hash不在trustcache

                                                                            if ( _codeDirectoryHashInCompilationServiceHash(v37) && (unsigned int)noEntitlementsPresent(v13) )
                                                                             {
                                                                            LOBYTE(v126[0]) = 0;
                                                                            v41 = current_proc();
                                                                            AppleMobileFileIntegrity::AMFIEntitlementGetBool(
                                                                            v41,
                                                                            (proc *)"com.apple.private.amfi.can-execute-cdhash",
                                                                            (const char *)v126,
                                                                            v42);
                                                                            if ( LOBYTE(v126[0]) )
                                                                            {
                                                                            v30 = postValidation(a1, v13, *a3, "can-execute-cdhash", a7, a8);
                                                                            v40 = v30 ^ 1;                         // 非(platform binary并且hash type为sha256)
                                                                            }
                                                                            else
                                                                            {
                                                                            IOLog("AMFI: can-execute-cdhash code in non-entitled context.\n");
                                                                            v40 = 1;
                                                                            }
                                                                            v31 = 0LL;
                                                                            goto LABEL_81;
                                                                            }

                                                                                首先判断这个cd hash如果在compilationServiceHash里,并且进程所含的entitlements不为空,如果进程的entitlements包含com.apple.private.amfi.can-execute-cdhash,并且这个进程并非platform binary,则完成此次判断直接返回。compilationServiceHash由用户态进程调用AMFI loadCompilationServiceCodeDirectoryHash接口进行设置,请参考3.3节。

                                                                              addr = (unsigned int *)csblob_get_addr(v112);
                                                                               size = csblob_get_size(v112);
                                                                               blob_bytes = csblob_find_blob_bytes(addr, size, 00xFADE0C02);// CSMAGIC_CODEDIRECTORY
                                                                               if ( !blob_bytes )
                                                                               {
                                                                               printf("AMFI: '%s' has no cd?\n"v48);
                                                                               v123[1] = 0LL;
                                                                              LABEL_63:
                                                                               printf("AMFI: '%s': Unrecoverable CT signature issue, bailing out.\n");
                                                                               goto LABEL_79;
                                                                               }

                                                                                  接着搜索cs_blob结构是否包含CSMAGIC_CODEDIRECTORY,如果不存在则直接返回。

                                                                                 v52 = (__int64)blob_bytes;
                                                                                 v108 = a1;
                                                                                 v53 = bswap32(blob_bytes[1]);               // CS_GenericBlob.length
                                                                                 if ( v53 <= 7 )
                                                                                 {
                                                                                 printf("AMFI: '%s' has short cd (%u)?\n");
                                                                                LABEL_62:
                                                                                 v123[1] = 0LL;
                                                                                 goto LABEL_63;
                                                                                 }

                                                                                    CODEDIRECTORY结构不能小于7

                                                                                  v54 = (unsigned int *)csblob_get_addr(v112);
                                                                                  v55 = csblob_get_size(v112);
                                                                                  v56 = csblob_find_blob_bytes(v54, v55, 0x100000xFADE0B01);// CSMAGIC_BLOBWRAPPER  CMS Signature
                                                                                  if ( !v56 )
                                                                                   {
                                                                                  printf("AMFI: '%s' has no CMS blob?\n");
                                                                                  goto LABEL_62;
                                                                                  }

                                                                                      继续搜索cs_blob是否包含CSMAGIC_BLOBWRAPPER,它会携带证书信息。

                                                                                     if ( v58 <= 7 )                             // CS_GenericBlob.length
                                                                                     {
                                                                                     printf("AMFI: '%s' has short cms (%u), bailing out.\n");
                                                                                     goto LABEL_62;
                                                                                     }

                                                                                        Cms结构不能小于7

                                                                                       if ( v57 == 0x8000000 )                     // CS_GenericBlob.length == 0
                                                                                       {
                                                                                       if ( (unsigned int)CTEvaluateAMFICodeSignatureCMS(
                                                                                       v59,
                                                                                       v100,
                                                                                       v52,
                                                                                       v53,
                                                                                       v60,
                                                                                       0LL,
                                                                                       0LL,
                                                                                       (__int64 *)&v123[1],
                                                                                       &v117,
                                                                                       &v116,
                                                                                       v123,
                                                                                       (__int64)&v122) )
                                                                                       {
                                                                                       v123[1] = 0LL;
                                                                                       printf("AMFI: '%s' does not pass CT evaluation, result: %#x\n");
                                                                                       goto LABEL_63;
                                                                                       }
                                                                                      }

                                                                                          CTEvaluateAMFICodeSignatureCMS函数验证cms携带的证书是否由苹果公司签发。

                                                                                        if ( (_DWORD)v116 )
                                                                                         {
                                                                                        hashtype = csblob_get_hashtype(v112);
                                                                                        v70 = (const unsigned __int8 *)v123[0];
                                                                                        if ( (_DWORD)v116 == 1 )
                                                                                        {
                                                                                        v71 = 1;
                                                                                        v61 = v108;
                                                                                        }
                                                                                        else
                                                                                        {
                                                                                        v61 = v108;
                                                                                        if ( (_DWORD)v116 != 4 )
                                                                                        {
                                                                                        printf("AMFI: '%s' has unknown CT digest type %#x\n");
                                                                                        goto LABEL_149;
                                                                                        }
                                                                                        v71 = 2;
                                                                                        }
                                                                                        if ( v71 == hashtype )
                                                                                        {
                                                                                        if ( v122 > 19 )
                                                                                        {
                                                                                        if ( !memcmp(v46v123[0], 0x14uLL) )
                                                                                        goto LABEL_70;
                                                                                        *(_QWORD *)&v94 = 0xAAAAAAAAAAAAAAAALL;
                                                                                        *((_QWORD *)&v94 + 1) = 0xAAAAAAAAAAAAAAAALL;
                                                                                        *(_OWORD *)&v125[25] = v94;
                                                                                        *(_OWORD *)v125 = v94;
                                                                                        *(_OWORD *)&v125[16] = v94;
                                                                                        *(_OWORD *)&v127[9] = v94;
                                                                                        *(_OWORD *)v126 = v94;
                                                                                        *(_OWORD *)v127 = v94;
                                                                                        cdhash_to_string(v125v46);
                                                                                        cdhash_to_string((char *)v126v70);
                                                                                        printf("AMFI: '%s': cdhash mismatch: actual '%s' != expected '%s'\n");
                                                                                        }
                                                                                        else
                                                                                        {
                                                                                        printf("AMFI: '%s' has unexpected digest data len %zu (type %#x)\n");
                                                                                        }
                                                                                        }
                                                                                        else
                                                                                        {
                                                                                        printf("AMFI: '%s' has unexpected digest type, actual %#x != expected %#x\n");
                                                                                        }
                                                                                        LABEL_149:
                                                                                        printf("AMFI: '%s': V2 hash agility validation failed, bailing out.\n");
                                                                                        goto LABEL_63;
                                                                                         }

                                                                                            如果v116不为空,还要判断CTEvaluateAMFICodeSignatureCMS返回的cd hash值是否与当前csblobcd hash值是否相同,不相同则验证失败返回。

                                                                                          LABEL_70:
                                                                                          if ( ((unsigned __int64)v123[1] & 0x1FF80) == 0 )
                                                                                          {
                                                                                          printf("AMFI: '%s': unsuitable CT policy %#llx for this platform/device, rejecting signature.\n");
                                                                                          goto LABEL_79;
                                                                                          }

                                                                                              v123[1]CTEvaluateAMFICodeSignatureCMS返回的某个标志值。

                                                                                            if ( ((__int64)v123[1] & 0x5100) != 0 && allow3rdParty && !IsCDHashBlacklisted(v46))
                                                                                            {
                                                                                            IOLog("App Store Fast Path -> %s\n", v48);
                                                                                            v64 = *a3 | 0x4000;                    // CS_ENTITLEMENTS_VALIDATED
                                                                                            *a3 = v64;
                                                                                            v15 = v107;
                                                                                            v65 = v103;
                                                                                            v63 = v105;
                                                                                            goto LABEL_123;
                                                                                            }
                                                                                            *a3 |= 0x20000000u; // CS_SIGNED

                                                                                              0x5100代表app由应用市场签发,对cs_flags标志设置 CS_ENTITLEMENTS_VALIDATED|CS_SIGNED,然后直接返回。

                                                                                               可以看到iosapp应用也可以使能CDHashBlacklisted这样可以将某个appcd hash加入黑名单,就可以防止它运行。

                                                                                              if ( !allow3rdParty)
                                                                                              {
                                                                                              printf("AMFI: '%s' does not pass kernel-side policy for this model, bailing out.\n", v48);
                                                                                              v62 = 0;
                                                                                              v66 = 0;
                                                                                              v63 = v105;
                                                                                              LABEL_120:
                                                                                              if ( !(v66 | v62) )
                                                                                              {
                                                                                              if ( v102 && !v113 )
                                                                                              {
                                                                                              printf("AMFI: code signature validation failed.\n");
                                                                                              printf("AMFI: bailing out because of restricted entitlements.\n");
                                                                                              v30 = fatal_error_fmt(
                                                                                               v61,
                                                                                               v105,
                                                                                               v103,
                                                                                               "Code has restricted entitlements, but the validation of its code signature failed.\n"
                                                                                               "Unsatisfied Entitlements: %s",
                                                                                               v31);
                                                                                              goto LABEL_80;
                                                                                              }
                                                                                              LABEL_79:
                                                                                              v30 = printf("AMFI: code signature validation failed.\n");
                                                                                              LABEL_80:
                                                                                              v40 = 1;
                                                                                              v15 = v107;
                                                                                              goto LABEL_81;
                                                                                              }
                                                                                              if ( !v66 )
                                                                                              {
                                                                                              v30 = printf("AMFI: code signature validation failed.\n");
                                                                                              v15 = v107;
                                                                                              if ( v62 )
                                                                                              {
                                                                                              IOLog("AMFI: detected an anomaly during entitlement parsing.\n");
                                                                                              v30 = fatal_error_fmt(v61, v63, v103, "anomaly detecting during entitlement parsing\n");
                                                                                              }
                                                                                              v40 = 1;
                                                                                              goto LABEL_81;
                                                                                              }
                                                                                              v64 = *a3;
                                                                                              v15 = v107;
                                                                                              v65 = v103;
                                                                                              LABEL_123:
                                                                                              v30 = postValidation(v61, v112, v64, "dynamic", v63, v65);
                                                                                              v40 = v30 ^ 1;
                                                                                              goto LABEL_81;
                                                                                              }

                                                                                                  如果内核配置为不允许加载三方应用,则打印一些出错信息,直接返回。

                                                                                                if ( (csblob_get_flags(v112) & 2) == 0 )  // CS_ADHOC
                                                                                                {
                                                                                                printf("AMFI: '%s' has zero length cms, but is not adhoc?\n");
                                                                                                goto LABEL_63;
                                                                                                }
                                                                                                printf("AMFI: '%s' is adhoc signed.\n"v48);
                                                                                                v61 = v108;

                                                                                                    如果允许三方应用,则判断cs_flags是否为CS_ADHOC,不是的话则直接返回。

                                                                                                   接下来要与用户态进程amfid交互,来判断CS_ADHOC进程是否合法。

                                                                                                  if ( (unsigned int)getDaemonPort(v123) )
                                                                                                  {
                                                                                                  IOLog("StaticPlatformPolicy<%d>: no registered daemon port\n", 2LL);
                                                                                                  v62 = 0;
                                                                                                  v63 = v105;
                                                                                                  LABEL_116:
                                                                                                  v86 = 0;
                                                                                                  LABEL_117:
                                                                                                  if ( v62 )
                                                                                                  v66 = 0;
                                                                                                  else
                                                                                                  v66 = v86;
                                                                                                  goto LABEL_120;
                                                                                                  }

                                                                                                      获取afmid进程的portmach微内核通过port进行进程间通讯。

                                                                                                    v91 = verify_code_directory(
                                                                                                     (__int64)v89,
                                                                                                     (__int64)v61 + 8,
                                                                                                     base_offset,
                                                                                                     v110,
                                                                                                     v88,
                                                                                                     v113 != 0,
                                                                                                     2LL,
                                                                                                     v116,
                                                                                                     v117,
                                                                                                     (_DWORD *)&v118 + 1,
                                                                                                     &v120,
                                                                                                     &v119,
                                                                                                     v109,
                                                                                                     (_DWORD *)&v121 + 1,
                                                                                                     &v121,
                                                                                                     &v118,
                                                                                                     v31,
                                                                                                     (__int64)&v123[1],
                                                                                                     v126);

                                                                                                      调用verify_code_directory让用户态amfid去做校验。

                                                                                                       if ( v92 == -308 )
                                                                                                       {
                                                                                                      IOLog("StaticPlatformPolicy<%d>: verify_code_directory server is dead\n");
                                                                                                       }
                                                                                                       else if ( v92 )
                                                                                                       {
                                                                                                      IOLog("StaticPlatformPolicy<%d>: verify_code_directory returned 0x%x\n");
                                                                                                       }

                                                                                                          当用户态amfid进程返回出错码,则直接返回。

                                                                                                        *(_OWORD *)v125 = *(_OWORD *)v126;
                                                                                                         *(_OWORD *)&v125[16] = *(_OWORD *)v127;
                                                                                                         if ( (unsigned int)tokenIsTrusted((audit_token_t *)v125) )
                                                                                                         {
                                                                                                         v62 = v118 != 0;
                                                                                                         if ( v120 == 1 )
                                                                                                         {
                                                                                                         if ( !memcmp(v68, &v123[1], 20uLL) )
                                                                                                         {
                                                                                                         if ( HIDWORD(v121) )
                                                                                                         panic("\"amfid is broken. (%s) (%d) (%d) (%d)\"", (const char *)v61 + 8, v88, v120, HIDWORD(v121));
                                                                                                         if ( (_DWORD)v121 )
                                                                                                         *a3 |= 0x40000000u;             // CS_DEV_CODE
                                                                                                         if ( v119 )
                                                                                                         *a3 &= ~0x800u;                 // CS_RESTRICT
                                                                                                         v87 = BYTE4(v118);
                                                                                                         if ( HIDWORD(v118) )
                                                                                                         {
                                                                                                         v87 = 0;
                                                                                                         *a3 |= 0x4200u;                 // CS_KILL|CS_ENTITLEMENTS_VALIDATED
                                                                                                         }
                                                                                                         v86 = 1;
                                                                                                        LABEL_115:
                                                                                                         v63 = v105;
                                                                                                         if ( (v87 & 1) == 0 )
                                                                                                         goto LABEL_117;
                                                                                                         goto LABEL_116;
                                                                                                         }
                                                                                                         IOLog("%s: Possible race detected. Rejecting.\n", (const char *)v61 + 8);
                                                                                                        IOLog("StaticPlatformPolicy<%d>: Unable to match identity\n"2LL);

                                                                                                            当用户态afmid返回成功时,会先调用tokenIsTrusted传递当前进程的audit_token,它根据audit_token找到当前进程的cd hash是否在static trustcache中,以此证明当前内核确实是与amfid进程通讯,同时amfid会返回给内核一个cd hash值,如果与当前进程的cd hash值相同,则验证通过。根据不同的返回值设置相应的cs_flags,然后返回。

                                                                                                        3.2.3 额外的几个服务函数

                                                                                                          AppleMobileFileIntegrityUserClient::externalMethod(unsigned int, IOExternalMethodArguments *, IOExternalMethodDispatch *, OSObject *, void *)::methods
                                                                                                          DCQ __ZN34AppleMobileFileIntegrityUserClient14loadTrustCacheEP8OSObjectPvP25IOExternalMethodArguments
                                                                                                          DCQ __ZN34AppleMobileFileIntegrityUserClient19flushAllValidationsEP8OSObjectPvP25IOExternalMethodArguments
                                                                                                          DCQ __ZN34AppleMobileFileIntegrityUserClient39loadCompilationServiceCodeDirectoryHashEP8OSObjectPvP25IOExternalMethodArguments
                                                                                                          DCQ __ZN34AppleMobileFileIntegrityUserClient20isCdhashInTrustCacheEP8OSObjectPvP25IOExternalMethodArguments
                                                                                                          DCQ __ZN34AppleMobileFileIntegrityUserClient14loadTrustCacheEP8OSObjectPvP25IOExternalMethodArguments
                                                                                                          DCQ __ZN34AppleMobileFileIntegrityUserClient17validateSignatureEP8OSObjectPvP25IOExternalMethodArguments
                                                                                                          DCQ __ZN34AppleMobileFileIntegrityUserClient11setDenylistEP8OSObjectPvP25IOExternalMethodArguments

                                                                                                          3.2.3.1 loadTrustCache

                                                                                                            _int64 __fastcall AppleMobileFileIntegrityUserClient::loadTrustCache(
                                                                                                            AppleMobileFileIntegrityUserClient *this,
                                                                                                            OSObject *a2,
                                                                                                            void *a3,
                                                                                                            IOExternalMethodArguments *a4)
                                                                                                            {
                                                                                                             v6 = proc_selfpid();
                                                                                                            IOLog(
                                                                                                            "%s: PID %d is requesting a trust cache load\n",
                                                                                                            "static IOReturn AppleMobileFileIntegrityUserClient::loadTrustCache(OSObject *, void *, IOExternalMethodArguments *)",
                                                                                                            v6);

                                                                                                                每次加载都产生一次日志。

                                                                                                              Bool = AppleMobileFileIntegrity::AMFIEntitlementGetBool(
                                                                                                              v9,
                                                                                                              (proc *)"com.apple.private.amfi.can-load-trust-cache",
                                                                                                              &v30,
                                                                                                              v10);
                                                                                                              proc_rele(v11);
                                                                                                               if ( Bool )
                                                                                                               {
                                                                                                              proc_selfpid();
                                                                                                              IOLog("%s: failure getting load trust cache entitlement for process %d\n");
                                                                                                              return 3758097089LL;
                                                                                                               }
                                                                                                               if ( !v30 )
                                                                                                               {
                                                                                                              proc_selfpid();
                                                                                                              IOLog("%s: process %d not allowed to load trust cache\n");
                                                                                                              return 3758097089LL;
                                                                                                               }

                                                                                                                  判断当前进程是否由com.apple.private.amfi.can-load-trust-cache这个entitlement

                                                                                                                v17 = *((unsigned int *)a3 + 1);
                                                                                                                 if ( (_DWORD)v17 == 7 )
                                                                                                                 {

                                                                                                                    V17代表trustcache类型,7表示携带manifest信息。

                                                                                                                  v19 = AppleMobileFileIntegrityUserClient::copyTrustCacheAndManifestFromInputArgument(
                                                                                                                  (AppleMobileFileIntegrityUserClient *)a3,
                                                                                                                  (IOExternalMethodArguments *)&v28,
                                                                                                                  (void **)&v29,
                                                                                                                  (unsigned __int64 *)&v26,
                                                                                                                  (void **)&v27,
                                                                                                                  &v24,
                                                                                                                  &v25,
                                                                                                                  v14);
                                                                                                                  v15 = v19;
                                                                                                                   TrustCacheWithExternalManifest = AppleMobileFileIntegrity::loadTrustCacheWithExternalManifest(
                                                                                                                   v8,
                                                                                                                   v26,
                                                                                                                   v27,
                                                                                                                   (void *)v24,
                                                                                                                  (unsigned __int64)v25);

                                                                                                                      copyTrustCacheAndManifestFromInputArgument函数做格式检查,最后调用loadTrustCacheWithExternalManifest函数装载trustcache

                                                                                                                      如果v17不为7,代表trustcache不携带manifest信息,调用loadTrustCache函数进行装载。

                                                                                                                      loadTrustCacheWithExternalManifestloadTrustCache最终会调用loadTrustCacheWithExternalManifest函数,它又进一步调用pmap_load_image4_trust_cache,这个函数是ppl提供的服务函数。

                                                                                                                  它的功能是将一个imag4类型的trustcache保存到ppl中。

                                                                                                                  第一个参数指向struct image4_trust_cache结构体,第二个参数为它的大小,第三个参数为image4 manifest buffer,结构不详,第四个参数为manifest buffer大小。

                                                                                                                    __PPLTEXT:__text:FFFFFFF0097FE078 BL              _pmap_reserve_ppl_page
                                                                                                                    __PPLTEXT:__text:FFFFFFF0097FE07C CMP             W0, #6
                                                                                                                    __PPLTEXT:__text:FFFFFFF0097FE080 B.NE            loc_FFFFFFF0097FE08C
                                                                                                                    首先调用_pmap_reserve_ppl_page从ppl中获取一个page,然后挂接到_pmap_reserved_page_list中。
                                                                                                                    __PPLTEXT:__text:FFFFFFF0097FE08C loc_FFFFFFF0097FE08C ; CODE XREF: _pmap_load_image4_trust_cache_internal+48↑j
                                                                                                                    __PPLTEXT:__text:FFFFFFF0097FE08C MOV             W8, #0x10000000
                                                                                                                    __PPLTEXT:__text:FFFFFFF0097FE090 CMP             X21, X8
                                                                                                                    __PPLTEXT:__text:FFFFFFF0097FE094 B.LS            loc_FFFFFFF0097FE0A0 ; x1 < 0x10000000
                                                                                                                    __PPLTEXT:__text:FFFFFFF0097FE098 MOV             W25, #0xFFFFFFFA
                                                                                                                    __PPLTEXT:__text:FFFFFFF0097FE09C B               loc_FFFFFFF0097FE30C
                                                                                                                    __PPLTEXT:__text:FFFFFFF0097FE0A0 ; ---------------------------------------------------------------------------
                                                                                                                    __PPLTEXT:__text:FFFFFFF0097FE0A0
                                                                                                                    __PPLTEXT:__text:FFFFFFF0097FE0A0 loc_FFFFFFF0097FE0A0 ; CODE XREF: _pmap_load_image4_trust_cache_internal+5C↑j
                                                                                                                    __PPLTEXT:__text:FFFFFFF0097FE0A0 MOV             W8, #0x10000000
                                                                                                                    __PPLTEXT:__text:FFFFFFF0097FE0A4 CMP             X19, X8
                                                                                                                    __PPLTEXT:__text:FFFFFFF0097FE0A8 B.LS            loc_FFFFFFF0097FE0B4 ; x3 < 0x10000000
                                                                                                                    __PPLTEXT:__text:FFFFFFF0097FE0AC MOV             W25, #0xFFFFFFF8
                                                                                                                    __PPLTEXT:__text:FFFFFFF0097FE0B0 B               loc_FFFFFFF0097FE30C
                                                                                                                    __PPLTEXT:__text:FFFFFFF0097FE0B4 ; -----------------------------------------------------
                                                                                                                    __PPLTEXT:__text:FFFFFFF0097FE0B4 loc_FFFFFFF0097FE0B4 ; CODE XREF: _pmap_load_image4_trust_cache_internal+70↑j
                                                                                                                    __PPLTEXT:__text:FFFFFFF0097FE0B4 CMN             X22, X21
                                                                                                                    __PPLTEXT:__text:FFFFFFF0097FE0B8 B.CS            loc_FFFFFFF0097FE514 ; x0 + x1 < x0
                                                                                                                    __PPLTEXT:__text:FFFFFFF0097FE0BC CMN             X20, X19
                                                                                                                    __PPLTEXT:__text:FFFFFFF0097FE0C0 B.CS            loc_FFFFFFF0097FE524 ; x3 + x4 < x3
                                                                                                                    __PPLTEXT:__text:FFFFFFF0097FE0C4 TST             X22, #0x3FFF
                                                                                                                    __PPLTEXT:__text:FFFFFFF0097FE0C8 B.NE            loc_FFFFFFF0097FE534 ; x0 & 0x3fff
                                                                                                                    __PPLTEXT:__text:FFFFFFF0097FE0CC TST             X21, #0x3FFF ; x1 & 0x3fff
                                                                                                                    __PPLTEXT:__text:FFFFFFF0097FE0D0 B.NE            loc_FFFFFFF0097FE544
                                                                                                                    __PPLTEXT:__text:FFFFFFF0097FE0D4 TST             X20, #0x3FFF
                                                                                                                    __PPLTEXT:__text:FFFFFFF0097FE0D8 B.NE            loc_FFFFFFF0097FE554 ; x2 & 0x3fff
                                                                                                                    __PPLTEXT:__text:FFFFFFF0097FE0DC TST             X19, #0x3FFF
                                                                                                                    __PPLTEXT:__text:FFFFFFF0097FE0E0 B.NE            loc_FFFFFFF0097FE564 ; x3 & 0x3fff
                                                                                                                    __PPLTEXT:__text:FFFFFFF0097FE0E4 CMP             X21, #0x57 ; 'W'
                                                                                                                    __PPLTEXT:__text:FFFFFFF0097FE0E8 B.LS            loc_FFFFFFF0097FE574 ; x1 < 0x57

                                                                                                                        一个image4_trust_cache和其manifest buffer地址必须以0x4000对齐,并且大小不能超过0x10000000 image4_trust_cache不能小于0x57

                                                                                                                      __PPLTEXT:__text:FFFFFFF0097FE0EC MOV             X0, X22
                                                                                                                      __PPLTEXT:__text:FFFFFFF0097FE0F0 MOV             X1, X21
                                                                                                                      __PPLTEXT:__text:FFFFFFF0097FE0F4 BL              _pmap_ppl_lockdown_pages
                                                                                                                      __PPLTEXT:__text:FFFFFFF0097FE0F8 CBZ             X19, loc_FFFFFFF0097FE108
                                                                                                                      __PPLTEXT:__text:FFFFFFF0097FE0FC MOV             X0, X20
                                                                                                                      __PPLTEXT:__text:FFFFFFF0097FE100 MOV             X1, X19
                                                                                                                      __PPLTEXT:__text:FFFFFFF0097FE104 BL              _pmap_ppl_lockdown_pages

                                                                                                                       调用_pmap_ppl_lockdown_pagesppl中分别锁定image4_trust_cachemanifest buffer物理页。

                                                                                                                        __PPLTEXT:__text:FFFFFFF0097FE128 BL              _pmap_get_current_csidentity

                                                                                                                            获取调用进程的csidentity

                                                                                                                          __PPLTEXT:__text:FFFFFFF0097FE130 ADRL            X8, aComAppleMobile ; "com.apple.mobile.softwareupdated"
                                                                                                                          __PPLTEXT:__text:FFFFFFF0097FE138 MOV             X9, X0
                                                                                                                          __PPLTEXT:__text:FFFFFFF0097FE13C
                                                                                                                          __PPLTEXT:__text:FFFFFFF0097FE13C loc_FFFFFFF0097FE13C ; CODE XREF: _pmap_load_image4_trust_cache_internal+11C↓j
                                                                                                                          __PPLTEXT:__text:FFFFFFF0097FE13C LDRB            W10, [X9]
                                                                                                                          __PPLTEXT:__text:FFFFFFF0097FE140 LDRB            W11, [X8] ; "com.apple.mobile.softwareupdated"
                                                                                                                          __PPLTEXT:__text:FFFFFFF0097FE144 CMP             W10, W11
                                                                                                                          __PPLTEXT:__text:FFFFFFF0097FE148 B.NE            loc_FFFFFFF0097FE16C
                                                                                                                          __PPLTEXT:__text:FFFFFFF0097FE14C ADD             X8, X8, #1
                                                                                                                          __PPLTEXT:__text:FFFFFFF0097FE150 ADD             X9, X9, #1
                                                                                                                          __PPLTEXT:__text:FFFFFFF0097FE154 CBNZ            W10, loc_FFFFFFF0097FE13C
                                                                                                                          __PPLTEXT:__text:FFFFFFF0097FE158

                                                                                                                           通过上述代码判断进程的csidentity是否为com.apple.mobile.softwareupdatedcom.apple.load_trust_cachecom.apple.security.cryptexdcom.apple.MobileStorageMounter

                                                                                                                          com.apple.security.cryptexctl。如果进程csidentity不是这些中的一个,则直接返回。

                                                                                                                            __PPLTEXT:__text:FFFFFFF0097FE22C loc_FFFFFFF0097FE22C ; CODE XREF: _pmap_load_image4_trust_cache_internal+128↑j
                                                                                                                            __PPLTEXT:__text:FFFFFFF0097FE22C ; _pmap_load_image4_trust_cache_internal+164↑j ...
                                                                                                                            __PPLTEXT:__text:FFFFFFF0097FE22C ADD             X27, X22, #0x58 ; 'X'
                                                                                                                            __PPLTEXT:__text:FFFFFFF0097FE230 LDR             X1, [X22,#0x50]
                                                                                                                            __PPLTEXT:__text:FFFFFFF0097FE234 ADD             X6, SP, #0x70+var_58 ; uuid
                                                                                                                            __PPLTEXT:__text:FFFFFFF0097FE238 ADD             X7, SP, #0x70+var_60
                                                                                                                            __PPLTEXT:__text:FFFFFFF0097FE23C MOV             X0, X27
                                                                                                                            __PPLTEXT:__text:FFFFFFF0097FE240 MOV             X2, X20
                                                                                                                            __PPLTEXT:__text:FFFFFFF0097FE244 MOV             X3, X24
                                                                                                                            __PPLTEXT:__text:FFFFFFF0097FE248 MOV             W4, #0x6C747273
                                                                                                                            __PPLTEXT:__text:FFFFFFF0097FE250 MOV             X5, X26
                                                                                                                            __PPLTEXT:__text:FFFFFFF0097FE254 BL              _pmap_validate_image4
                                                                                                                            __PPLTEXT:__text:FFFFFFF0097FE258 CBZ             W0, loc_FFFFFFF0097FE2A4
                                                                                                                            __PPLTEXT:__text:FFFFFFF0097FE25C MOV             X25, X0
                                                                                                                            __PPLTEXT:__text:FFFFFFF0097FE260 CBZ             W28, loc_FFFFFFF0097FE2EC
                                                                                                                            __PPLTEXT:__text:FFFFFFF0097FE264 CMP             W26, #1
                                                                                                                            __PPLTEXT:__text:FFFFFFF0097FE268 B.NE            loc_FFFFFFF0097FE2EC
                                                                                                                            __PPLTEXT:__text:FFFFFFF0097FE26C CMP             W25, W28
                                                                                                                            __PPLTEXT:__text:FFFFFFF0097FE270 B.NE            loc_FFFFFFF0097FE2EC
                                                                                                                            __PPLTEXT:__text:FFFFFFF0097FE274 LDR             X1, [X22,#0x50]
                                                                                                                            __PPLTEXT:__text:FFFFFFF0097FE278 ADD             X6, SP, #0x70+var_58
                                                                                                                            __PPLTEXT:__text:FFFFFFF0097FE27C ADD             X7, SP, #0x70+var_60
                                                                                                                            __PPLTEXT:__text:FFFFFFF0097FE280 MOV             X0, X27
                                                                                                                            __PPLTEXT:__text:FFFFFFF0097FE284 MOV             X2, X20
                                                                                                                            __PPLTEXT:__text:FFFFFFF0097FE288 MOV             X3, X24
                                                                                                                            __PPLTEXT:__text:FFFFFFF0097FE28C MOV             W4, #0x6C747273
                                                                                                                            __PPLTEXT:__text:FFFFFFF0097FE294 MOV             W5, #4
                                                                                                                            __PPLTEXT:__text:FFFFFFF0097FE298 BL              _pmap_validate_image4
                                                                                                                            __PPLTEXT:__text:FFFFFFF0097FE29C MOV             X25, X0
                                                                                                                            __PPLTEXT:__text:FFFFFFF0097FE2A0 CBNZ            W0, loc_FFFFFFF0097FE2EC

                                                                                                                                调用_pmap_validate_image4验证image4_trust_cache和其manifest buffer结构合法性,var_58保存的是image4uuid

                                                                                                                              __PPLTEXT:__text:FFFFFFF0097FE330 loc_FFFFFFF0097FE330 ; CODE XREF: _pmap_load_image4_trust_cache_internal+2A4↑j
                                                                                                                              __PPLTEXT:__text:FFFFFFF0097FE330 ADR             X24, _pmap_loaded_trust_caches_lock
                                                                                                                              __PPLTEXT:__text:FFFFFFF0097FE334 NOP
                                                                                                                              __PPLTEXT:__text:FFFFFFF0097FE338 MOV             X0, X24
                                                                                                                              __PPLTEXT:__text:FFFFFFF0097FE33C BL              _hw_lock_lock_nopreempt
                                                                                                                              __PPLTEXT:__text:FFFFFFF0097FE340 ADRP            X27, #_pmap_image4_trust_caches@PAGE
                                                                                                                              __PPLTEXT:__text:FFFFFFF0097FE344 LDR             X8, [X27,#_pmap_image4_trust_caches@PAGEOFF]
                                                                                                                              __PPLTEXT:__text:FFFFFFF0097FE348 CBZ             X8, loc_FFFFFFF0097FE478
                                                                                                                              __PPLTEXT:__text:FFFFFFF0097FE34C LDR             X9, [SP,#0x70+var_58]
                                                                                                                              __PPLTEXT:__text:FFFFFFF0097FE350 B               loc_FFFFFFF0097FE35C ; struct trust_cache_entry1 {

                                                                                                                                  pmap_image4_trust_caches数组保存的是ppl中所有的image4结构体内容,xnu源码在某个版本中泄露过struct image4_trust_cache的结构体。

                                                                                                                                osfmk/kern/trustcache.h
                                                                                                                                struct trust_cache_entry1 {
                                                                                                                                uint8_t cdhash[CS_CDHASH_LEN];
                                                                                                                                uint8_t hash_type;
                                                                                                                                uint8_t flags;
                                                                                                                                } __attribute__((__packed__));
                                                                                                                                struct trust_cache_module1 {
                                                                                                                                uint32_t version;
                                                                                                                                uuid_t uuid;
                                                                                                                                uint32_t num_entries;
                                                                                                                                struct trust_cache_entry1 entries[];
                                                                                                                                } __attribute__((__packed__));
                                                                                                                                osfmk/vm/pmap.h
                                                                                                                                #define PMAP_IMAGE4_TRUST_CACHE_HAS_TYPE 1
                                                                                                                                struct pmap_image4_trust_cache {
                                                                                                                                 // Filled by pmap layer.
                                                                                                                                 struct pmap_image4_trust_cache const *next;            // linked list linkage
                                                                                                                                 struct trust_cache_module1 const *module;                       // pointer into module (within data below)
                                                                                                                                 // Filled by caller.
                                                                                                                                 // data is either an image4,
                                                                                                                                 // or just the trust cache payload itself if the image4 manifest is external.
                                                                                                                                 pmap_tc_type_t type;
                                                                                                                                 size_t bnch_len;
                                                                                                                                 uint8_t const bnch[48];
                                                                                                                                 size_t data_len;
                                                                                                                                 uint8_t const data[];
                                                                                                                                };

                                                                                                                                  __PPLTEXT:__text:FFFFFFF0097FE354 loc_FFFFFFF0097FE354 ; CODE XREF: _pmap_load_image4_trust_cache_internal+334↓j
                                                                                                                                  __PPLTEXT:__text:FFFFFFF0097FE354 ; _pmap_load_image4_trust_cache_internal+344↓j ...
                                                                                                                                  __PPLTEXT:__text:FFFFFFF0097FE354 LDR             X8, [X8] ; cache_entry = cache_entry->next
                                                                                                                                  __PPLTEXT:__text:FFFFFFF0097FE358 CBZ             X8, loc_FFFFFFF0097FE478
                                                                                                                                  __PPLTEXT:__text:FFFFFFF0097FE35C
                                                                                                                                  __PPLTEXT:__text:FFFFFFF0097FE35C loc_FFFFFFF0097FE35C ; CODE XREF: _pmap_load_image4_trust_cache_internal+318↑j
                                                                                                                                  __PPLTEXT:__text:FFFFFFF0097FE35C LDR             X10, [X8,#8] ; module = cache_entry->module
                                                                                                                                  __PPLTEXT:__text:FFFFFFF0097FE360 LDRB            W11, [X9,#4]
                                                                                                                                  __PPLTEXT:__text:FFFFFFF0097FE364 LDRB            W12, [X10,#4] ; module->uuid[0]
                                                                                                                                  __PPLTEXT:__text:FFFFFFF0097FE368 CMP             W11, W12
                                                                                                                                  __PPLTEXT:__text:FFFFFFF0097FE36C B.NE            loc_FFFFFFF0097FE354 ; cache_entry = cache_entry->next
                                                                                                                                  __PPLTEXT:__text:FFFFFFF0097FE370 LDRB            W11, [X9,#5]
                                                                                                                                  __PPLTEXT:__text:FFFFFFF0097FE374 LDRB            W12, [X10,#5] ; module->uuid[1]
                                                                                                                                  __PPLTEXT:__text:FFFFFFF0097FE378 CMP             W11, W12
                                                                                                                                  __PPLTEXT:__text:FFFFFFF0097FE37C B.NE            loc_FFFFFFF0097FE354 ; cache_entry = cache_entry->next
                                                                                                                                  __PPLTEXT:__text:FFFFFFF0097FE380 LDRB            W11, [X9,#6]
                                                                                                                                  __PPLTEXT:__text:FFFFFFF0097FE384 LDRB            W12, [X10,#6] ; module->uuid[2]
                                                                                                                                  __PPLTEXT:__text:FFFFFFF0097FE388 CMP             W11, W12
                                                                                                                                  __PPLTEXT:__text:FFFFFFF0097FE38C B.NE            loc_FFFFFFF0097FE354 ; cache_entry = cache_entry->next
                                                                                                                                  __PPLTEXT:__text:FFFFFFF0097FE390 LDRB            W11, [X9,#7]
                                                                                                                                  __PPLTEXT:__text:FFFFFFF0097FE394 LDRB            W12, [X10,#7] ; module->uuid[3]
                                                                                                                                  __PPLTEXT:__text:FFFFFFF0097FE398 CMP             W11, W12
                                                                                                                                  __PPLTEXT:__text:FFFFFFF0097FE39C B.NE            loc_FFFFFFF0097FE354 ; cache_entry = cache_entry->next
                                                                                                                                  __PPLTEXT:__text:FFFFFFF0097FE3A0 LDRB            W11, [X9,#8]
                                                                                                                                  __PPLTEXT:__text:FFFFFFF0097FE3A4 LDRB            W12, [X10,#8] ; module->uuid[4]
                                                                                                                                  __PPLTEXT:__text:FFFFFFF0097FE3A8 CMP             W11, W12
                                                                                                                                  __PPLTEXT:__text:FFFFFFF0097FE3AC B.NE            loc_FFFFFFF0097FE354 ; cache_entry = cache_entry->next
                                                                                                                                  __PPLTEXT:__text:FFFFFFF0097FE3B0 LDRB            W11, [X9,#9]
                                                                                                                                  __PPLTEXT:__text:FFFFFFF0097FE3B4 LDRB            W12, [X10,#9] ; module->uuid[5]
                                                                                                                                  __PPLTEXT:__text:FFFFFFF0097FE3B8 CMP             W11, W12
                                                                                                                                  __PPLTEXT:__text:FFFFFFF0097FE3BC B.NE            loc_FFFFFFF0097FE354 ; cache_entry = cache_entry->next
                                                                                                                                  __PPLTEXT:__text:FFFFFFF0097FE3C0 LDRB            W11, [X9,#0xA]
                                                                                                                                  __PPLTEXT:__text:FFFFFFF0097FE3C4 LDRB            W12, [X10,#0xA] ; module->uuid[6]
                                                                                                                                  __PPLTEXT:__text:FFFFFFF0097FE3C8 CMP             W11, W12
                                                                                                                                  __PPLTEXT:__text:FFFFFFF0097FE3CC B.NE            loc_FFFFFFF0097FE354 ; cache_entry = cache_entry->next
                                                                                                                                  __PPLTEXT:__text:FFFFFFF0097FE3D0 LDRB            W11, [X9,#0xB]
                                                                                                                                  __PPLTEXT:__text:FFFFFFF0097FE3D4 LDRB            W12, [X10,#0xB] ; module->uuid[7]
                                                                                                                                  __PPLTEXT:__text:FFFFFFF0097FE3D8 CMP             W11, W12
                                                                                                                                  __PPLTEXT:__text:FFFFFFF0097FE3DC B.NE            loc_FFFFFFF0097FE354 ; cache_entry = cache_entry->next
                                                                                                                                  __PPLTEXT:__text:FFFFFFF0097FE3E0 LDRB            W11, [X9,#0xC]
                                                                                                                                  __PPLTEXT:__text:FFFFFFF0097FE3E4 LDRB            W12, [X10,#0xC] ; module->uuid[8]
                                                                                                                                  __PPLTEXT:__text:FFFFFFF0097FE3E8 CMP             W11, W12
                                                                                                                                  __PPLTEXT:__text:FFFFFFF0097FE3EC B.NE            loc_FFFFFFF0097FE354 ; cache_entry = cache_entry->next
                                                                                                                                  __PPLTEXT:__text:FFFFFFF0097FE3F0 LDRB            W11, [X9,#0xD]
                                                                                                                                  __PPLTEXT:__text:FFFFFFF0097FE3F4 LDRB            W12, [X10,#0xD] ; module->uuid[9]
                                                                                                                                  __PPLTEXT:__text:FFFFFFF0097FE3F8 CMP             W11, W12
                                                                                                                                  __PPLTEXT:__text:FFFFFFF0097FE3FC B.NE            loc_FFFFFFF0097FE354 ; cache_entry = cache_entry->next
                                                                                                                                  __PPLTEXT:__text:FFFFFFF0097FE400 LDRB            W11, [X9,#0xE]
                                                                                                                                  __PPLTEXT:__text:FFFFFFF0097FE404 LDRB            W12, [X10,#0xE] ; module->uuid[10]
                                                                                                                                  __PPLTEXT:__text:FFFFFFF0097FE408 CMP             W11, W12
                                                                                                                                  __PPLTEXT:__text:FFFFFFF0097FE40C B.NE            loc_FFFFFFF0097FE354 ; cache_entry = cache_entry->next
                                                                                                                                  __PPLTEXT:__text:FFFFFFF0097FE410 LDRB            W11, [X9,#0xF]
                                                                                                                                  __PPLTEXT:__text:FFFFFFF0097FE414 LDRB            W12, [X10,#0xF] ; module->uuid[11]
                                                                                                                                  __PPLTEXT:__text:FFFFFFF0097FE418 CMP             W11, W12
                                                                                                                                  __PPLTEXT:__text:FFFFFFF0097FE41C B.NE            loc_FFFFFFF0097FE354 ; cache_entry = cache_entry->next
                                                                                                                                  __PPLTEXT:__text:FFFFFFF0097FE420 LDRB            W11, [X9,#0x10]
                                                                                                                                  __PPLTEXT:__text:FFFFFFF0097FE424 LDRB            W12, [X10,#0x10] ; module->uuid[12]
                                                                                                                                  __PPLTEXT:__text:FFFFFFF0097FE428 CMP             W11, W12
                                                                                                                                  __PPLTEXT:__text:FFFFFFF0097FE42C B.NE            loc_FFFFFFF0097FE354 ; cache_entry = cache_entry->next
                                                                                                                                  __PPLTEXT:__text:FFFFFFF0097FE430 LDRB            W11, [X9,#0x11]
                                                                                                                                  __PPLTEXT:__text:FFFFFFF0097FE434 LDRB            W12, [X10,#0x11] ; module->uuid[13]
                                                                                                                                  __PPLTEXT:__text:FFFFFFF0097FE438 CMP             W11, W12
                                                                                                                                  __PPLTEXT:__text:FFFFFFF0097FE43C B.NE            loc_FFFFFFF0097FE354 ; cache_entry = cache_entry->next
                                                                                                                                  __PPLTEXT:__text:FFFFFFF0097FE440 LDRB            W11, [X9,#0x12]
                                                                                                                                  __PPLTEXT:__text:FFFFFFF0097FE444 LDRB            W12, [X10,#0x12] ; module->uuid[14]
                                                                                                                                  __PPLTEXT:__text:FFFFFFF0097FE448 CMP             W11, W12
                                                                                                                                  __PPLTEXT:__text:FFFFFFF0097FE44C B.NE            loc_FFFFFFF0097FE354 ; cache_entry = cache_entry->next
                                                                                                                                  __PPLTEXT:__text:FFFFFFF0097FE450 LDRB            W11, [X9,#0x13]
                                                                                                                                  __PPLTEXT:__text:FFFFFFF0097FE454 LDRB            W10, [X10,#0x13] ; module->uuid[15]
                                                                                                                                  __PPLTEXT:__text:FFFFFFF0097FE458 CMP             W11, W10
                                                                                                                                  __PPLTEXT:__text:FFFFFFF0097FE45C B.NE            loc_FFFFFFF0097FE354 ; cache_entry = cache_entry->next

                                                                                                                                      通过一个循环判断要保存的image4 cache对应的uuid是否已经在pmap_image4_trust_caches数组中。

                                                                                                                                    __PPLTEXT:__text:FFFFFFF0097FE478 loc_FFFFFFF0097FE478 ; CODE XREF: _pmap_load_image4_trust_cache_internal+310↑j
                                                                                                                                    __PPLTEXT:__text:FFFFFFF0097FE478 ; _pmap_load_image4_trust_cache_internal+320↑j
                                                                                                                                    __PPLTEXT:__text:FFFFFFF0097FE478 MOV             X0, X22
                                                                                                                                    __PPLTEXT:__text:FFFFFFF0097FE47C BL              _mmu_kvtop
                                                                                                                                    __PPLTEXT:__text:FFFFFFF0097FE480 CBNZ            X0, loc_FFFFFFF0097FE498
                                                                                                                                    __PPLTEXT:__text:FFFFFFF0097FE484 ADRP            X8, #_kernel_pmap@PAGE
                                                                                                                                    __PPLTEXT:__text:FFFFFFF0097FE488 LDR             X0, [X8,#_kernel_pmap@PAGEOFF]
                                                                                                                                    __PPLTEXT:__text:FFFFFFF0097FE48C MOV             X1, X22
                                                                                                                                    __PPLTEXT:__text:FFFFFFF0097FE490 BL              _pmap_vtophys
                                                                                                                                    __PPLTEXT:__text:FFFFFFF0097FE494 LSL             X0, X0, #0xE
                                                                                                                                    __PPLTEXT:__text:FFFFFFF0097FE498
                                                                                                                                    __PPLTEXT:__text:FFFFFFF0097FE498 loc_FFFFFFF0097FE498 ; CODE XREF: _pmap_load_image4_trust_cache_internal+448↑j
                                                                                                                                    __PPLTEXT:__text:FFFFFFF0097FE498 BL              _phystokv
                                                                                                                                    __PPLTEXT:__text:FFFFFFF0097FE49C MOV             X25, X0 ; start
                                                                                                                                    __PPLTEXT:__text:FFFFFFF0097FE4A0 ADD             X26, X0, #4,LSL#12 ; end = x0 + 4 << 12
                                                                                                                                    __PPLTEXT:__text:FFFFFFF0097FE4A4 MOV             X1, X26
                                                                                                                                    __PPLTEXT:__text:FFFFFFF0097FE4A8 MOV             W2, #0xB
                                                                                                                                    __PPLTEXT:__text:FFFFFFF0097FE4AC MOV             W3, #1
                                                                                                                                    __PPLTEXT:__text:FFFFFFF0097FE4B0 BL              _pmap_set_range_xprr_perm ; (start, end, 0xb, 1)
                                                                                                                                    __PPLTEXT:__text:FFFFFFF0097FE4B4 LDR             X8, [SP,#0x70+var_58]
                                                                                                                                    __PPLTEXT:__text:FFFFFFF0097FE4B8 STR             X8, [X25,#8] ; *(start + 8) = var_58
                                                                                                                                    __PPLTEXT:__text:FFFFFFF0097FE4BC LDR             X8, [X27,#_pmap_image4_trust_caches@PAGEOFF]
                                                                                                                                    __PPLTEXT:__text:FFFFFFF0097FE4C0 STR             X8, [X25] ; *(start + 0) = _pmap_image4_trust_caches
                                                                                                                                    __PPLTEXT:__text:FFFFFFF0097FE4C4 MOV             X0, X25
                                                                                                                                    __PPLTEXT:__text:FFFFFFF0097FE4C8 MOV             X1, X26
                                                                                                                                    __PPLTEXT:__text:FFFFFFF0097FE4CC MOV             W2, #1
                                                                                                                                    __PPLTEXT:__text:FFFFFFF0097FE4D0 MOV             W3, #0xB
                                                                                                                                    __PPLTEXT:__text:FFFFFFF0097FE4D4 BL              _pmap_set_range_xprr_perm ; (start, end, 1, 0xb)
                                                                                                                                    __PPLTEXT:__text:FFFFFFF0097FE4F4 loc_FFFFFFF0097FE4F4 ; CODE XREF: _pmap_load_image4_trust_cache_internal+4A0↑j
                                                                                                                                    __PPLTEXT:__text:FFFFFFF0097FE4F4 STR             X22, [X27,#_pmap_image4_trust_caches@PAGEOFF] ; _pmap_image4_trust_caches = x0

                                                                                                                                      调用pmap_set_range_xprr_perm设置image4 cache的权限, Image4 cachenext字段设置为pmap_image4_trust_caches

                                                                                                                                    然后将image4 cache挂接为pmap_image4_trust_caches数组的第一个元素。

                                                                                                                                    3.2.3.2 flushAllValidations

                                                                                                                                      __int64 __fastcall AppleMobileFileIntegrityUserClient::flushAllValidations(
                                                                                                                                      AppleMobileFileIntegrityUserClient *this,
                                                                                                                                      OSObject *a2,
                                                                                                                                      void *a3,
                                                                                                                                      IOExternalMethodArguments *a4)
                                                                                                                                      {
                                                                                                                                       if ( v4 && (v5 = (AppleMobileFileIntegrity *)v4[27].__vftable) != 0LL )
                                                                                                                                      return AppleMobileFileIntegrity::purgeCachedValidationResults(v5);
                                                                                                                                      else
                                                                                                                                      return 3758097097LL;
                                                                                                                                      }
                                                                                                                                      __int64 __fastcall AppleMobileFileIntegrity::purgeCachedValidationResults(AppleMobileFileIntegrity *this)
                                                                                                                                      {
                                                                                                                                      cs_blob_reset_cache(this);
                                                                                                                                       denyUnrestrictedDebuggingCache = 0;
                                                                                                                                      return 0LL;
                                                                                                                                      }
                                                                                                                                      void cs_blob_reset_cache()
                                                                                                                                      {
                                                                                                                                      atomic_fetch_add_explicit(&cs_blob_generation_count, 2u, memory_order_relaxed);
                                                                                                                                      }

                                                                                                                                      3.2.3.3 loadCompilationServiceCodeDirectoryHash

                                                                                                                                        __int64 __fastcall AppleMobileFileIntegrityUserClient::loadCompilationServiceCodeDirectoryHash(
                                                                                                                                        AppleMobileFileIntegrityUserClient *this,
                                                                                                                                        OSObject *a2,
                                                                                                                                        void *a3,
                                                                                                                                        IOExternalMethodArguments *a4)
                                                                                                                                        {
                                                                                                                                        Bool = AppleMobileFileIntegrity::AMFIEntitlementGetBool(
                                                                                                                                         v10,
                                                                                                                                         (proc *)"com.apple.private.amfi.can-load-cdhash",
                                                                                                                                         &v15,
                                                                                                                                         v11);
                                                                                                                                        proc_rele(v12);

                                                                                                                                            判断当前进程是否有com.apple.private.amfi.can-load-cdhash这个entitlement

                                                                                                                                          return AppleMobileFileIntegrity::loadCompilationServiceCodeDirectoryHash(v6, v16);

                                                                                                                                              调用loadCompilationServiceCodeDirectoryHash装载compilationServiceHash

                                                                                                                                            __int64 __fastcall AppleMobileFileIntegrity::loadCompilationServiceCodeDirectoryHash(
                                                                                                                                             AppleMobileFileIntegrity *this,
                                                                                                                                             const unsigned __int8 *a2)
                                                                                                                                            {
                                                                                                                                             __int128 v3; // q0
                                                                                                                                             lck_mtx_lock((IOLock *)compServiceHashLock);
                                                                                                                                             v3 = *(_OWORD *)a2;
                                                                                                                                             dword_FFFFFFF009A584B0 = *((_DWORD *)a2 + 4);
                                                                                                                                             compilationServiceHash = v3;
                                                                                                                                             lck_mtx_unlock((IOLock *)compServiceHashLock);
                                                                                                                                             return 0LL;
                                                                                                                                            }

                                                                                                                                            3.2.3.4 isCdhashInTrustCache

                                                                                                                                              __int64 __fastcall AppleMobileFileIntegrityUserClient::isCdhashInTrustCache(
                                                                                                                                              AppleMobileFileIntegrityUserClient *this,
                                                                                                                                              OSObject *a2,
                                                                                                                                              void *a3,
                                                                                                                                              IOExternalMethodArguments *a4)
                                                                                                                                              {
                                                                                                                                              Bool = AppleMobileFileIntegrity::isCdhashInTrustCache(
                                                                                                                                               v6,
                                                                                                                                               *((unsigned __int8 **)a3 + 6),
                                                                                                                                               *((_QWORD *)a3 + 4) != 0LL);
                                                                                                                                              proc_selfpid();
                                                                                                                                              IOLog("%s: Returning IOReturn 0x%x to process %d\n");

                                                                                                                                                  调用isCdhashInTrustCache检查用户态进程传递进来的cd hash是否在trustcache中。

                                                                                                                                                __int64 __fastcall amfi_is_cdhash_in_trust_cache(constunsigned __int8 *a1, int a2)
                                                                                                                                                {
                                                                                                                                                __int64 result; // x0
                                                                                                                                                result = AMFIIsCodeDirectoryInTrustCache(a1);
                                                                                                                                                 if ( (result & 1) == 0 )
                                                                                                                                                 {
                                                                                                                                                if ( a2 )
                                                                                                                                                return (unsigned int)codeDirectoryHashIsInLoadedTrustCache(a1) != 0;
                                                                                                                                                 }
                                                                                                                                                return result;
                                                                                                                                                }

                                                                                                                                                   AMFIIsCodeDirectoryInTrustCachecodeDirectoryHashIsInLoadedTrustCache调用ppl服务函数,检查cd hash是否在static trustcacheloaded trustcache中。

                                                                                                                                                3.2.3.5 validateSignature

                                                                                                                                                    AMFI允许用户进程主动传递一个cs_blob来做验签,最终都会调用check_signature函数,参见第2节。

                                                                                                                                                3.2.3.6 setDenylist

                                                                                                                                                  __int64 __fastcall AppleMobileFileIntegrityUserClient::setDenylist(
                                                                                                                                                  AppleMobileFileIntegrityUserClient *this,
                                                                                                                                                  OSObject *a2,
                                                                                                                                                  void *a3,
                                                                                                                                                  IOExternalMethodArguments *a4)
                                                                                                                                                  {
                                                                                                                                                  Bool = AppleMobileFileIntegrity::AMFIEntitlementGetBool(
                                                                                                                                                  v6,
                                                                                                                                                  (proc *)"com.apple.private.amfi.can-set-denylist",
                                                                                                                                                  &v18,
                                                                                                                                                  v7);

                                                                                                                                                      判断当前进程是否有com.apple.private.amfi.can-set-denylist这个entitlement

                                                                                                                                                     if ( (ValidateBlacklist(v15, v11) & 1) != 0 )
                                                                                                                                                     {
                                                                                                                                                     UpdateBlacklist(v15, v11);
                                                                                                                                                     return 0LL;
                                                                                                                                                     }

                                                                                                                                                        ValidateBlacklist函数判断要检查的cd hash是否已在blacklist中。

                                                                                                                                                      void __fastcall UpdateBlacklist(__int64 a1, __int64 a2)
                                                                                                                                                      {
                                                                                                                                                      lck_rw_lock_exclusive((IORWLock *)blacklistLock);
                                                                                                                                                       IOFree((void *)blacklist, 32 * blacklistSize);
                                                                                                                                                       blacklist = a1;
                                                                                                                                                       blacklistSize = a2;
                                                                                                                                                      lck_rw_done((IORWLock *)blacklistLock);
                                                                                                                                                      }

                                                                                                                                                          释放原有blacklist内存,更新blacklist新内存。

                                                                                                                                                      3.2.4 用户态查询与设置接口

                                                                                                                                                        内核提供了csops_internal函数用于接收用户态参数,对以下进程的签名信息进行设置与读取。

                                                                                                                                                        Bsd/sys/codesign.h
                                                                                                                                                        /* csopsoperations */
                                                                                                                                                        #define CS_OPS_STATUS 0       /* return status */
                                                                                                                                                        #define CS_OPS_MARKINVALID 1      /* invalidate process */
                                                                                                                                                        #define CS_OPS_MARKHARD 2      /* set HARD flag */
                                                                                                                                                        #define CS_OPS_MARKKILL 3      /* set KILL flag (sticky) */
                                                                                                                                                        #ifdef KERNEL_PRIVATE
                                                                                                                                                        /* CS_OPS_PIDPATH 4       */
                                                                                                                                                        #endif
                                                                                                                                                        #define CS_OPS_CDHASH 5       /* get code directory hash */
                                                                                                                                                        #define CS_OPS_PIDOFFSET 6      /* get offset of active Mach-o slice */
                                                                                                                                                        #define CS_OPS_ENTITLEMENTS_BLOB 7 /* get entitlements blob */
                                                                                                                                                        #define CS_OPS_MARKRESTRICT 8      /* set RESTRICT flag (sticky) */
                                                                                                                                                        #define CS_OPS_SET_STATUS 9      /* set codesign flags */
                                                                                                                                                        #define CS_OPS_BLOB 10      /* get codesign blob */
                                                                                                                                                        #define CS_OPS_IDENTITY 11     /* get codesign identity */
                                                                                                                                                        #define CS_OPS_CLEARINSTALLER 12     /* clear INSTALLER flag */
                                                                                                                                                        #define CS_OPS_CLEARPLATFORM 13 /* clear platform binary status (DEVELOPMENT-only) */
                                                                                                                                                        #define CS_OPS_TEAMID 14 /* get team id */
                                                                                                                                                        #define CS_OPS_CLEAR_LV 15 /* clear the library validation flag */
                                                                                                                                                        #define CS_OPS_DER_ENTITLEMENTS_BLOB 16 /* get der entitlements blob */

                                                                                                                                                        3.3 PPL保护

                                                                                                                                                        3.3.1 pmap_cs_cd_register_internal

                                                                                                                                                            ioscode directory存放到ppl进行保护。ppl中的服务函数为pmap_cs_cd_register_internal,它的功能是将每个进程的csblob结构体放到ppl中进行保护,同时将其部分内容算sha256哈希,存放于一个特殊的cd结构体中,这个结构体又被红黑树管理起来。

                                                                                                                                                            第一个参数为struct cs_blob结构体,第二个参数为struct cs_blob结构体的长度,第三个参数为struct cs_blob结构里的某个offset,这个函数将csblob结构体保存在ppl中,

                                                                                                                                                            同时计算struct cs_bloboffset开始的hash并存放于ppl中,第四、五个参数功能不详。

                                                                                                                                                            由于这个函数非常复杂,只贴出部分关键操作的代码。

                                                                                                                                                          MOV X9, #0xFFFFFFE000000000
                                                                                                                                                          CMP X0, X9
                                                                                                                                                          B.CC loc_FFFFFFF0097FDA8C ; x0 < 0xFFFFFFE000000000
                                                                                                                                                          TBNZ W8, #0, loc_FFFFFFF0097FDA8C ; x0 + x1 < x0
                                                                                                                                                          ADD X9, X20, X21
                                                                                                                                                          MOV X8, #0xFFFFFFFC00000000
                                                                                                                                                          CMP X9, X8
                                                                                                                                                          B.CS loc_FFFFFFF0097FDA8C ; x0 + x1 > 0xFFFFFFFC00000000
                                                                                                                                                          MOV X24, X2
                                                                                                                                                          ADDS X8, X2, X21
                                                                                                                                                          CSET W10, CS
                                                                                                                                                          SUBS X26, X20, X2 ; offset = x1 - x2
                                                                                                                                                          B.CC loc_FFFFFFF0097FDA9C ; offset < 0
                                                                                                                                                          TBNZ W10, #0, loc_FFFFFFF0097FDA9C ; x0 + x2 < x0
                                                                                                                                                          CMP X8, X9
                                                                                                                                                          B.HI loc_FFFFFFF0097FDA9C ; x1 + x2 > x1 + x0

                                                                                                                                                              上述代码对前三个参数做了范围检查。

                                                                                                                                                            MOV W8, #0x1FF8
                                                                                                                                                            CMP X20, X8
                                                                                                                                                            STR X4, [SP,#0xD0+var_B0]
                                                                                                                                                            B.HI loc_FFFFFFF0097FD3AC ; x1 > 0x1ff8 x23 = 0
                                                                                                                                                            CMP X20, #0x1F9
                                                                                                                                                            B.CC loc_FFFFFFF0097FD3B4 ; x1 < 0x1f9 x8 = 0
                                                                                                                                                            CMP X20, #0x3F9 ; x1 < 0x3f9 x8 = 1
                                                                                                                                                            B.CC loc_FFFFFFF0097FD3BC
                                                                                                                                                            CMP X20, #0x7F9
                                                                                                                                                            B.CC loc_FFFFFFF0097FD3C4 ; x1 < 0x7f9 x8 = 2
                                                                                                                                                            CMP X20, #0xFF8
                                                                                                                                                            MOV W8, #3
                                                                                                                                                            CINC W8, W8, HI
                                                                                                                                                            B loc_FFFFFFF0097FD3C8 ; if x1 < 0xff8
                                                                                                                                                            x8 = 3
                                                                                                                                                            else
                                                                                                                                                            x8 = 4
                                                                                                                                                            MOV X23, #0
                                                                                                                                                            B loc_FFFFFFF0097FD4B8
                                                                                                                                                            MOV W8, #0
                                                                                                                                                            B loc_FFFFFFF0097FD3C8
                                                                                                                                                            MOV W8, #1
                                                                                                                                                            B loc_FFFFFFF0097FD3C8
                                                                                                                                                            MOV W8, #2

                                                                                                                                                                根据第2个参数计算出一个index,它是struct csblob _pmap_cs_blob_free_list[]数组的一个索引。在xnu的源码bsd/sys/ubc_internal.h中可以看到struct csblob根据config的不同,

                                                                                                                                                            有不同的大小。

                                                                                                                                                              MOV W22, W8
                                                                                                                                                              LSL X27, X22, #3 ; index = w8 * 8
                                                                                                                                                              ADR X25, _pmap_cs_blob_free_list_lock
                                                                                                                                                              NOP
                                                                                                                                                              MOV X0, X25
                                                                                                                                                              BL _hw_lock_lock_nopreempt
                                                                                                                                                              ADR X8, _pmap_cs_blob_free_list ; uint64_t _pmap_cs_blob_free_list[]
                                                                                                                                                              NOP
                                                                                                                                                              LDR X23, [X8,X27] ; csblob_struct =_pmap_cs_blob_free_list[index]
                                                                                                                                                              CBZ X23, loc_FFFFFFF0097FD428
                                                                                                                                                              LDR X9, [X23] ; next = csblob_struct->next
                                                                                                                                                              STR X9, [X8,X22,LSL#3] ; _pmap_cs_blob_free_list[index]=next
                                                                                                                                                              STR X19, [X23] ; csblob_struct->next=0xDEADEAD0A110CED
                                                                                                                                                              LSL X8, X22, #2 ; index1=w8 * 4
                                                                                                                                                              ADR X9, _pmap_cs_blob_free_count
                                                                                                                                                              NOP
                                                                                                                                                              LDR W10, [X9,X8]
                                                                                                                                                              SUB W10, W10, #1
                                                                                                                                                              STR W10, [X9,X8] ; _pmap_cs_blob_free_count[index1] -= 1

                                                                                                                                                                根据index,从_pmap_cs_blob_free_list找到一个空闲的块, pmap_cs_blob_free_count数组保存的是对应的cs_blob大小的空闲数目。

                                                                                                                                                                如果_pmap_cs_blob_free_list对应的节点为空,则要申请一个新的page

                                                                                                                                                                MRS X8, #0, c13, c0, #4
                                                                                                                                                                LDR W8, [X8,#0x500]
                                                                                                                                                                CBZ W8, loc_FFFFFFF0097FDB14
                                                                                                                                                                STLR XZR, [X25]
                                                                                                                                                                BL _pmap_get_free_ppl_page
                                                                                                                                                                CBZ X0, loc_FFFFFFF0097FD900
                                                                                                                                                                MOV X23, X0 ; new_blob_page
                                                                                                                                                                ADRL X8, _pmap_cs_blob_bins
                                                                                                                                                                LDR X25, [X8,X27] ; bin_size = _pmap_cs_blob_bins[index]
                                                                                                                                                                BL _phystokv
                                                                                                                                                                MOV W27, #0x4000
                                                                                                                                                                MOV W1, #0x4000 ; size_t
                                                                                                                                                                BL _bzero  ; bzero(new_blog_page, 0x4000)
                                                                                                                                                                MOV X0, X23
                                                                                                                                                                BL _phystokv
                                                                                                                                                                MOV X23, X0
                                                                                                                                                                MOV X1, #0
                                                                                                                                                                MOV X11, #0
                                                                                                                                                                STR X19, [X0] ; ((csblob_struct *)new_blob_page)->next = 0xDEADEAD0A110CED
                                                                                                                                                                AND X8, X25, #0xFFFFFFFFFFFFFFF8 ; bin_size &= 0xFFFFFFFFFFFFFFF8
                                                                                                                                                                MOV W9, #2  ; x9 = 2
                                                                                                                                                                UDIV X10, X27, X25 ; num = 0x4000 / bin_size

                                                                                                                                                                    _pmap_get_free_ppl_pageppl中申请了一个新的物理页,通过_phystokv转化为虚拟地址,然后用bzero进行初始化。前面将csblob有不同的大小,对一个新分配好的page要对它进行切块分配,自然要知道每个块的大小,_pmap_cs_blob_bins数组保存的就是对应的csblob大小。最后x10保存的是一个page对应的csblob块大小,然后通过一个循环对其进行初始化。

                                                                                                                                                                  ADD X0, X0, X8 ; current_page = new_blob_page
                                                                                                                                                                   ; next_page = 0
                                                                                                                                                                   ; do {
                                                                                                                                                                   ; current_page += bin_size
                                                                                                                                                                  STR X11, [X0] ; *(current_page->next) = next_page
                                                                                                                                                                  CMP X1, #0
                                                                                                                                                                  CSEL X1, X0, X1, EQ ; if (x1 == 0)
                                                                                                                                                                  x1 = x0
                                                                                                                                                                  ADD W12, W9, #1
                                                                                                                                                                  CMP X10, W9,UXTW
                                                                                                                                                                  MOV X9, X12 ; x9 += 1
                                                                                                                                                                  MOV X11, X0 ; next_page = current_page
                                                                                                                                                                  B.HI loc_FFFFFFF0097FD484 ; while (num > x9)
                                                                                                                                                                  SUB W3, W12, #2 ; num -= 2
                                                                                                                                                                  MOV X2, X22 ; index
                                                                                                                                                                  MOV W4, #1
                                                                                                                                                                  BL _pmap_cs_blob_add_to_free_list

                                                                                                                                                                      这个循环是对每个csblob的第一个成员,也就是next指针进行初始化,注意每个page的第一个csblob是保留不用的,没有被链接起来,它的next是指向0xDEADEAD0A110CED

                                                                                                                                                                    最后通过_pmap_cs_blob_add_to_free_list把这些块加入到现有链表中。

                                                                                                                                                                    __int64 __fastcall pmap_cs_blob_add_to_free_list(void *a1, _QWORD *a2, __int64 a3, __int64 a4, __int64 a5)
                                                                                                                                                                    {
                                                                                                                                                                     v7 = pmap_cs_blob_bins[(unsigned int)a3];
                                                                                                                                                                     if ( ((unsigned __int16)a1 & 0x3FFFu) % v7 || ((unsigned __int16)a2 & 0x3FFFu) % v7 )
                                                                                                                                                                    panic("\"free blob to add unaligned: (%p,%p)/%zu\"", a1, a2, v7);
                                                                                                                                                                     v11 = (unsigned int)a3;
                                                                                                                                                                    result = hw_lock_lock_nopreempt(&pmap_cs_blob_free_list_lock);
                                                                                                                                                                     *a2 = *(&pmap_cs_blob_free_list + v11);
                                                                                                                                                                    *(&pmap_cs_blob_free_list + v11) = a1;
                                                                                                                                                                     v13 = pmap_cs_blob_free_count[v11];
                                                                                                                                                                    pmap_cs_blob_free_count[v11] = v13 + a4;
                                                                                                                                                                     if ( __CFADD__(v13, (_DWORD)a4) )
                                                                                                                                                                    panic("\"blob free count overflow (bin %d, %d/%d)\"", a3, (unsigned int)pmap_cs_blob_free_count[v11], a4);
                                                                                                                                                                     v14 = pmap_cs_blob_page_count[v11];
                                                                                                                                                                    pmap_cs_blob_page_count[v11] = v14 + a5;
                                                                                                                                                                    }

                                                                                                                                                                        上述代码将链表头链接到新page的最有一个块,然后更新对应的free节点数目。

                                                                                                                                                                      MRS X8, #0, c13, c0, #4
                                                                                                                                                                      LDR W8, [X8,#0x500]
                                                                                                                                                                      CBZ W8, loc_FFFFFFF0097FDAC4
                                                                                                                                                                      STLR XZR, [X25]
                                                                                                                                                                      MOV W8, #0x1FF9
                                                                                                                                                                      CMP X20, X8
                                                                                                                                                                      B.CS loc_FFFFFFF0097FD5E4 ; x1 > 0x1ff9
                                                                                                                                                                      ADD X25, X23, #8
                                                                                                                                                                      MOV X0, X25 ; __dst
                                                                                                                                                                      MOV X1, X21 ; __src
                                                                                                                                                                      MOV X2, X20 ; __n
                                                                                                                                                                      BL _memmove ; memmove(new_blog_page + 8, x0, x1)

                                                                                                                                                                          使用memove将第一个参数指向的csblob结构体拷贝到刚申请到内存块中。

                                                                                                                                                                        ADR X25, _pmap_cs_cd_free_list_lock
                                                                                                                                                                        NOP
                                                                                                                                                                        MOV X0X25
                                                                                                                                                                        BL _hw_lock_lock_nopreempt
                                                                                                                                                                        ADRP X22#_pmap_cs_cd_free_list@PAGE
                                                                                                                                                                        LDR X28, [X22,#_pmap_cs_cd_free_list@PAGEOFF]
                                                                                                                                                                        ADRP X27#_pmap_cs_cd_free_count@PAGE
                                                                                                                                                                        CBZ X28, loc_FFFFFFF0097FD4F4 ; _pmap_cs_cd_free_list == 0
                                                                                                                                                                        LDR X8, [X28] ; next = *pmap_cs_cd_free_list
                                                                                                                                                                        STR X8, [X22,#_pmap_cs_cd_free_list@PAGEOFF] ; pmap_cs_cd_free_list = next
                                                                                                                                                                        STR X19, [X28] ; *next = 0xDEADEAD0A110CED
                                                                                                                                                                        LDR W8, [X27,#_pmap_cs_cd_free_count@PAGEOFF]
                                                                                                                                                                        SUB W8W8#1
                                                                                                                                                                        STR  W8, [X27,#_pmap_cs_cd_free_count@PAGEOFF] ; _pmap_cs_cd_free_count[index] -= 1

                                                                                                                                                                            在获取一个csblob结构体后,接着要从_pmap_cs_cd_free_list链表分配一个cd结构体,注意它是不同于struct __CodeDirectory,是ppl内部使用的一个结构体。

                                                                                                                                                                          MRS X8, #0, c13, c0, #4
                                                                                                                                                                          LDR W8, [X8,#0x500]
                                                                                                                                                                          CBZ W8, loc_FFFFFFF0097FDADC
                                                                                                                                                                          STLR XZR, [X25]
                                                                                                                                                                          BL _pmap_get_free_ppl_page
                                                                                                                                                                          CBZ X0, loc_FFFFFFF0097FD648
                                                                                                                                                                          MOV X22, X0 ; new_cd_page
                                                                                                                                                                          BL _phystokv
                                                                                                                                                                          MOV W1, #0x4000 ; size_t
                                                                                                                                                                          BL _bzero  ; bzero(new_cd_page, 0x4000)
                                                                                                                                                                          MOV X0, X22
                                                                                                                                                                          BL _phystokv
                                                                                                                                                                          MOV X28, X0
                                                                                                                                                                          MOV X27, #0
                                                                                                                                                                          MOV X9, #0  ; next_page = 0
                                                                                                                                                                          MOV X22, X0 ; current_page = new_cd_page
                                                                                                                                                                          STR X19, [X22],#0x70 ; ((cscd_struct *)current_page)->next = 0xDEADEAD0A110CED
                                                                                                                                                                          __PPLTEXT:__text:FFFFFFF0097FD534 ; current_page += 0x70
                                                                                                                                                                          MOV W8, #0x91

                                                                                                                                                                              如果_pmap_cs_cd_free_list为空,则调用_pmap_get_free_ppl_page申请一个新的物理页,对其进行初始化,可以看到每个cd结构体的大小为0x91

                                                                                                                                                                            MOV X10, X22 ; tmp_page = current_page
                                                                                                                                                                            STR X9, [X22],#0x70 ; *(current_page->next) = next_page
                                                                                                                                                                            current_page += 0x70
                                                                                                                                                                            CMP X27, #0
                                                                                                                                                                            CSEL X27, X10, X27, EQ ; if (x27 == 0)
                                                                                                                                                                            first_page = tmp_page
                                                                                                                                                                            MOV X9, X10 ; next_page = current_page
                                                                                                                                                                            SUBS W8, W8, #1 ; w8 -= 1
                                                                                                                                                                            B.NE loc_FFFFFFF0097FD53C ; while (w8 != 0)

                                                                                                                                                                                循环对每个cdnext字段进行设置,同样第一个cd是保留不用的,next字段设置为0xDEADEAD0A110CED

                                                                                                                                                                              MOV X2, X25 ; size
                                                                                                                                                                              MOV X3, X24
                                                                                                                                                                              BL _hash
                                                                                                                                                                              ADD X0, SP#0xD0+__dst ; __dst
                                                                                                                                                                              ADD X1, SP#0xD0+__src ; __src
                                                                                                                                                                              MOV W2, #0x14 ; __n
                                                                                                                                                                              BL _memmove ; memmove(new_hash, x1, 0x14)

                                                                                                                                                                                  从csblob结构体的第2个参数起始位置算hash值。如果这个hash值在静态trustcache或动态trustcache中,还要做进一步合法性检查。

                                                                                                                                                                                STP WZR, W19, [X28,#0x50] ; *(new_cd_page + 0x50) = 0
                                                                                                                                                                                *(new_cd_page + 0x58) = flag
                                                                                                                                                                                MOV W8, #0x11
                                                                                                                                                                                STR X8, [X28,#0x48] ; *(new_cd_page + 0x11) = 0x11
                                                                                                                                                                                STR XZR, [X28,#0x40] ; *(new_cd_page + 0x40) = 0
                                                                                                                                                                                ADD X0, X28, #0x5C ; '\' ; __dst
                                                                                                                                                                                STR W26, [X28,#0x58]
                                                                                                                                                                                ADD X1, SP, #0xD0+__dst ; __src
                                                                                                                                                                                MOV W2, #0x14 ; __n
                                                                                                                                                                                BL _memmove ; memmove(new_cd_page + 0x5c, new_hash, 20)

                                                                                                                                                                                    将算出来的hash值保存在cd结构体的0x5c处。

                                                                                                                                                                                  ADR X8, _pmap_cs_registered_cds
                                                                                                                                                                                  NOP
                                                                                                                                                                                  STR X28, [X8] ; _pmap_cs_registered_cds = new_cd_page
                                                                                                                                                                                  ADR X0, _pmap_cs_registered_cds
                                                                                                                                                                                  NOP
                                                                                                                                                                                  MOV X1, X28
                                                                                                                                                                                  BL_pmap_cs_code_directories_RB_INSERT_COLOR

                                                                                                                                                                                      _pmap_cs_registered_cds变量保存的是cd结构体的红黑树,如果它为空,直接将cd结构体设为root节点,否则调用_pmap_cs_code_directories_RB_INSERT_COLOR将其插入到红黑树中。

                                                                                                                                                                                  3.3.2 zalloc只读内存保护

                                                                                                                                                                                  3.3.2.1 zalloc_ro_mut

                                                                                                                                                                                    __attribute__((noinline))
                                                                                                                                                                                    void
                                                                                                                                                                                    zalloc_ro_mut(zone_id_t zid, void *elem, vm_offset_t offset,
                                                                                                                                                                                    const void *new_data, vm_size_t new_data_size)

                                                                                                                                                                                    zalloc_ro_mut_validate_src(zid, elem, (vm_offset_t)new_data,
                                                                                                                                                                                         new_data_size);
                                                                                                                                                                                     pmap_ro_zone_memcpy(zid, (vm_offset_t) elem, offset,
                                                                                                                                                                                         (vm_offset_t) new_data, new_data_size);
                                                                                                                                                                                    }

                                                                                                                                                                                      zalloc_ro_mut函数用于将指定内存拷贝到只读内存,pmap_ro_zone_memcpy函数请求的是ppl中对应的服务函数,我们以最新的ios16为例进行逆向分析。

                                                                                                                                                                                      pmap_ro_zone_memcpy_ppl ; DATA XREF:
                                                                                                                                                                                      MOV X20, X4
                                                                                                                                                                                      MOV X22, X3
                                                                                                                                                                                      MOV X23, X2
                                                                                                                                                                                      MOV X21, X1
                                                                                                                                                                                      MOV X25, X0
                                                                                                                                                                                      ADD X24, X2, X1
                                                                                                                                                                                      MOV X0, X24 ; va + offset
                                                                                                                                                                                      BL kvtophys_nofail

                                                                                                                                                                                          首先调用kvtophys_nofailva + offset转为物理地址pa

                                                                                                                                                                                        MOV             X19X0 ; pa
                                                                                                                                                                                        ADRP X8#vm_first_phys@PAGE
                                                                                                                                                                                        LDR X8, [X8,#vm_first_phys@PAGEOFF]
                                                                                                                                                                                        ADRP X9#vm_last_phys@PAGE
                                                                                                                                                                                        LDR X9, [X9,#vm_last_phys@PAGEOFF]
                                                                                                                                                                                        CMP X8X0
                                                                                                                                                                                        CCMP X9X0#0, LS
                                                                                                                                                                                        B.LS loc_FFFFFFF008498A94 ; vm_first_phys < pa < vm_last_phys

                                                                                                                                                                                            接着判断物理地址pa是否在合法地址范围内。

                                                                                                                                                                                          CBZ X22, loc_FFFFFFF008498A78 ; new_data == NULL
                                                                                                                                                                                          CBZ X20, loc_FFFFFFF008498A78 ; new_data_size == 0

                                                                                                                                                                                              判断new_data是否为空, new_data_size是否为0

                                                                                                                                                                                            MOV X0, X25
                                                                                                                                                                                            MOV X1, X21
                                                                                                                                                                                            MOV X2, X23
                                                                                                                                                                                            MOV X3, X22
                                                                                                                                                                                            MOV X4, X20
                                                                                                                                                                                            BL pmap_ro_zone_validate_element ; (zid, va, offset, new_data, new_data_size)

                                                                                                                                                                                                调用pmap_ro_zone_validate_element函数做参数检查,在稍后会详细分析。

                                                                                                                                                                                              MOV X0, X19
                                                                                                                                                                                              MOV X1, X21
                                                                                                                                                                                              MOV X2, X20
                                                                                                                                                                                              BL pmap_ro_zone_lock_phy_page ; (pa, va, offset)
                                                                                                                                                                                              MOV X0, X19 ; vm_offset_t
                                                                                                                                                                                              BL _ml_static_ptovirt_0
                                                                                                                                                                                              MOV X1, X22 ; __src
                                                                                                                                                                                              MOV X2, X20 ; __n
                                                                                                                                                                                              BL _memmove ; (pa, new_data, new_data, size)

                                                                                                                                                                                                  可以看到,ppl直接使用memmove将目标内存拷贝进va对应的物理内存。Ppl并没有做请求来源的验证,这导致攻击者可以利用rop等技术直接调用此服务函数,将readonly内存改写为其他的内容。

                                                                                                                                                                                              接着,我们在仔细分析pmap_ro_zone_validate_element函数。

                                                                                                                                                                                                pmap_ro_zone_validate_element ; CODE XREF: pmap_ro_zone_bzero_ppl+6C↑p
                                                                                                                                                                                                ADDS X8, X3, X4
                                                                                                                                                                                                B.CS loc_FFFFFFF0084946E0 ; new_data + new_data_size < new_data
                                                                                                                                                                                                MOV X3, X4
                                                                                                                                                                                                B pmap_ro_zone_validate_element_dst ; (zid, va, offset, new_data_size)

                                                                                                                                                                                                    首先判断new_data + new_data_size是否溢出,然后调用pmap_ro_zone_validate_element_dst

                                                                                                                                                                                                  pmap_ro_zone_validate_element_dst ; CODE XREF:
                                                                                                                                                                                                  ADRL X9, zone_ro_elem_size
                                                                                                                                                                                                  ADD X8, X9, W0,UXTW#3
                                                                                                                                                                                                  LDR W8, [X8,#4] ; elem_size = zone_ro_elem_size[zid]
                                                                                                                                                                                                  ADRL X10, zone_info.zi_ro_range
                                                                                                                                                                                                  ADD X11, X10, #8
                                                                                                                                                                                                  LDP X10, X11, [X10] ; x10 = start
                                                                                                                                                                                                   ; x11 = end
                                                                                                                                                                                                  CMP X10, X1
                                                                                                                                                                                                  CCMP X11, X1, #0, LS
                                                                                                                                                                                                  B.LS            loc_FFFFFFF008494928 ; x10 < va < x11

                                                                                                                                                                                                      判断va是否在合法地址范围内,Readonly内存是从zone_info.zi_ro_range专有内存块分配的。

                                                                                                                                                                                                    MOV W10, W0
                                                                                                                                                                                                    LSL X10, X10, #3
                                                                                                                                                                                                    LDR W9, [X9,X10] ; elem_size1 = zone_ro_elem_size[zid << 3]
                                                                                                                                                                                                    AND W10, W1, #0x3FFF ; va &= 0x3fff
                                                                                                                                                                                                    MOV W11, #0x4000
                                                                                                                                                                                                    SUB W10, W11, W10 ; va = 0x4000 - va
                                                                                                                                                                                                    MUL W10, W9, W10 ; va *= elem_size1
                                                                                                                                                                                                    CMP W10, W9
                                                                                                                                                                                                    C. CS           loc_FFFFFFF008494928 ; va > elem_size1

                                                                                                                                                                                                        判断va是否跨page

                                                                                                                                                                                                      UBFX X9, X1, #0xE, #0x20 ; ' ' ; index = (uint43_t)(va >> 0xe)
                                                                                                                                                                                                      ADRP X10, #qword_FFFFFFF0077ED1D0@PAGE ;zone_info.zi_meta_base
                                                                                                                                                                                                      LDR X10, [X10,#qword_FFFFFFF0077ED1D0@PAGEOFF]
                                                                                                                                                                                                      LSL X9, X9, #4
                                                                                                                                                                                                      LDRH W9, [X10,X9]
                                                                                                                                                                                                      AND W9, W9, #0x3FF ; zm_index = (zone_info.zi_meta_base[index << 4] & 0x3ff)
                                                                                                                                                                                                      CMP W9, W0
                                                                                                                                                                                                      B.NE            loc_FFFFFFF008494928 ; meta->zm_index != zid

                                                                                                                                                                                                          判断va对应的Meta指向的zm_index是否与参数zid相等。

                                                                                                                                                                                                        SUB X9, X8, X2
                                                                                                                                                                                                        CMP X9, X3
                                                                                                                                                                                                        C. CC           loc_FFFFFFF0084948D0 ; elem_size - offset > new_data_size
                                                                                                                                                                                                        CMP X8, X2
                                                                                                                                                                                                        B.LS loc_FFFFFFF0084948FC ; elem_size < offset
                                                                                                                                                                                                        LDP X29, X30, [SP,#0x30+var_s0]
                                                                                                                                                                                                        ADD SP, SP, #0x40 ; '@'
                                                                                                                                                                                                        RETAB

                                                                                                                                                                                                        3.3.2.2 zalloc_ro_mut_atomic

                                                                                                                                                                                                          __attribute__((noinline))
                                                                                                                                                                                                          uint64_t
                                                                                                                                                                                                          zalloc_ro_mut_atomic(zone_id_t zid, void *elem, vm_offset_t offset,
                                                                                                                                                                                                          zro_atomic_op_t op, uint64_t value)
                                                                                                                                                                                                          {
                                                                                                                                                                                                           value = pmap_ro_zone_atomic_op(zid, (vm_offset_t)elem, offset, op, value);
                                                                                                                                                                                                           return value;
                                                                                                                                                                                                          }

                                                                                                                                                                                                          3.3.2.3 zalloc_ro_clear

                                                                                                                                                                                                            void
                                                                                                                                                                                                            zalloc_ro_clear(zone_id_t zid, void *elem, vm_offset_t offset, vm_size_t size)
                                                                                                                                                                                                            {
                                                                                                                                                                                                             pmap_ro_zone_bzero(zid, (vm_offset_t)elem, offset, size);
                                                                                                                                                                                                            }

                                                                                                                                                                                                              pmap_ro_zone_bzero_ppl ; DATA XREF:
                                                                                                                                                                                                              BL pmap_ro_zone_validate_element ;
                                                                                                                                                                                                              MOV X0, X19
                                                                                                                                                                                                              MOV X1, X21
                                                                                                                                                                                                              MOV X2, X20
                                                                                                                                                                                                              BL pmap_ro_zone_lock_phy_page
                                                                                                                                                                                                              MOV X0, X19 ; vm_offset_t
                                                                                                                                                                                                              BL _ml_static_ptovirt_0
                                                                                                                                                                                                              MOV X1, X20 ; size_t
                                                                                                                                                                                                              BL _bzero

                                                                                                                                                                                                                  逻辑与前面类似,只是调用了bzero

                                                                                                                                                                                                              阅读原文

                                                                                                                                                                                                              跳转微信打开