为了使代码签名证书正确配置并且代码签名真正有效,非常重要的一步就是要保证验证过程顺利执行,没有遗漏。当前签名验证是发生在内核模式下,而非用户模式下。签名的验证发生在两个阶段:加载可执行文件时、实际访问二进制代码时(Page Fault)。分成两个阶段也是出于性能方面的考虑,因为二进制文件是动态加载的,对于还没加载的部分仅当其加载如内存时,也即发生 Page Fault 时再进行签名验证。
1、可执行文件的加载
可执行文件的加载出现在execve()/mac_execve()或posix_spawn()系统调用被触发的时候。对于MachO,exec_mach_imgact()会被调用,在解析文件时它会找到LC_CODE_SIGNATURE的位置。代码签名二进制块会被加载到内核的统一高速缓存缓冲区中。
2、Page Fault时的处理
可以查看 osfmk/vm/vm_fault.c[5] 的代码
/*
* CODE SIGNING:
* When soft faulting a page, we have to validate the page if:
* 1. the page is being mapped in user space
* 2. the page hasn't already been found to be "tainted"
* 3. the page belongs to a code-signed object
* 4. the page has not been validated yet or has been mapped for write.
*/
#define VM_FAULT_NEED_CS_VALIDATION(pmap, page, page_obj) \
((pmap) != kernel_pmap /*1*/ && \
!(page)->cs_tainted /*2*/ && \
(page_obj)->code_signed /*3*/ && \
(!(page)->cs_validated || (page)->wpmapped /*4*/))
当 Page Fault 满足以上条件时将触发签名验证过程:
1、该页面正在用户空间中映射
2、这个页面还没有被发现为 tainted
3、该页属于一个代码签名对象
4、页面还没有被验证,或者还没有被映射为可写状态