Contents
  1. 1. Pointer Authentication
  2. 2. 新增的指令
    1. 2.1. PAC* 系列
    2. 2.2. AUT* 和 XPAC*
  3. 3. 使用的算法
  4. 4. 正向开发的挑战
    1. 4.1. crash
    2. 4.2. 还是 crash
  5. 5. 逆向的攻防
  6. 6. 参考

Pointer Authentication

指针签名。

64 位机型有大量 bit 位是没有存储信息的,xnu 写死最大 VA 地址是 0x0000000FC0000000ULL,用到 36 位就行了。
https://github.com/apple/darwin-xnu/blob/master/osfmk/mach/arm/vm_param.h#L157

指针签名可以用剩下的比特位来存储。当最高位 tag 位没使用时也可以用来存 pac,最高位 tag 位使用时那就是一个 tag pointer 了,不能用。在 macOS 上 tag 位是最低位,Mac 反正不用 arm 处理器也无所谓了。

objc 源码中搞了这种区分,最高位有效的 tag pointer mask 是 1<<63;最低位有效的 tag pointer mask 是 1。


从这些位操作来看,Mac 或者 iPhone 换 CPU 架构,下辈子吧。

新增的指令

PAC* 系列

原指针 + 上下文 + key = 加签指针
举个栗子,对程序返回地址的保护:
pacia lr, sp 用 sp 作为上下文,APIAKey 作为秘钥对 lr 加签,加签的结果存放到 lr 寄存器里。
authia lr, sp 用 sp 作为上下文,APIAKey 作为秘钥对 lr 验签,验签结果存放到 lr 寄存器里。
ret lr 就受到了保护。
还可以简写成 paciasp 等于 pacia lr, sp

新鲜出炉的 pac 异常,pacibsp。和 paciasp 功能一样,只不过用 b key 加密。

AUT* 和 XPAC*

aut* 就是验签,验证成功后把签名拿掉,得到的结果就是 original pointer;验证失败只能拿到一个 invalid pointer

xpac* 就是裁掉,把签名无差别擦掉,不做验证

使用的算法

苹果魔改过的 QARMA

正向开发的挑战

没啥挑战,dirty work 都在底层,正向开发感受不到。除了……

crash

PAC 验签失败导致的 crash 需要你认真甄别一下 https://developer.apple.com/documentation/security/preparing_your_app_to_work_with_pointer_authentication?language=objc

Exception Subtype: KERN_INVALID_ADDRESS at 0x0040000105394398 -> 0x0000000105394398 (possible pointer authentication failure)

比如这种 crash,苹果想告诉你有地址的 PAC 验签失败了,原地址是 0x0000000105394398,高位被改了,然后必然触发段错误,随后进崩溃基操流程。当然,这里的 PAC 肯定是事先被拿掉了,不拿掉容易被分析。

然后呢,苹果给了一些忠告

  • Return addresses are signed with a key that’s unique per process, using a salt derived from the stack pointer.

  • Function pointers are signed with a key that’s fixed across all processes, allowing sharing of library code between processes.

  • Virtual method table entries are signed with a key that’s shared across all apps, using a salt derived from the method signature.

  • 程序调用里的返回地址用栈指针加盐加签,key 是每个进程独享的,盐就是上下文。实际上就是 paciasp

  • 函数指针 key 是固定的,进程间共享。像这种情况的,内核 reboot 后 key 应该是要刷新了。

  • 虚拟方法表地址 xxxx 看不懂,没用过,C++ 才有的吧

还是 crash

主要是搜集 crash 的那些 Kit,碰到 PAC 的指针得裁一下。不然符号化失败。

看下巨硬的人给提的 PR https://github.com/microsoft/PLCrashReporter-Fork/pull/15/commits

看下是不是 64 位的,是的话直接拿 0x0000000fffffffff 把高位的裁掉。

uint64_tnormalizedInstructionPointer=lp64? (frameInfo.instructionPointer&0x0000000fffffffff) : frameInfo.instructionPointer;

逆向的攻防

https://googleprojectzero.blogspot.com/2019/02/examining-pointer-authentication-on.html
https://speakerdeck.com/marcograss/2pac-2furious-envisioning-an-ios-compromise-in-2019

参考

https://events.static.linuxfound.org/sites/events/files/slides/slides_23.pdf

Contents
  1. 1. Pointer Authentication
  2. 2. 新增的指令
    1. 2.1. PAC* 系列
    2. 2.2. AUT* 和 XPAC*
  3. 3. 使用的算法
  4. 4. 正向开发的挑战
    1. 4.1. crash
    2. 4.2. 还是 crash
  5. 5. 逆向的攻防
  6. 6. 参考