Arm64e 符号翻译与 PAC 问题

547次阅读  |  发布于4年以前

arm64e由于引入了PAC机制,导致符号地址发生了巨大变化。也给堆栈回溯带来了问题。

背景

从去年新iphone发布后,我们陆陆续续发现crash上报组件上报的crash总很奇怪,堆栈里要么只有栈帧0,要么就只有几个栈帧,而丢失了绝大部分的栈,起初我们没有在意,只是怀疑crash上报组件又引入了什么奇怪的bug,但因为量比较低,并不影响我们解决主要的crash问题(毕竟XS/XR设备还不是主流)。但是后来又遇到了一次QAPM上报的内存监控堆栈,发现也很奇怪堆栈还是丢失了很多,只有栈帧0或者main函数等信息,其它都只是符号地址,如下:

Thread 1 :
1 libsystem_kernel.dylib 0x1ab941000 0x1ab959c60
2 libsystem_kernel.dylib 0x1ab941000 0x1ab9590e8
3 unkonwn 0x57fe81ab7f7154
4 unkonwn 0x7dc001ab7f75f0
5 unkonwn 0x668381aba151f4
6 unkonwn 0x797701ac975b68
7 unkonwn 0x656881ac748400
8 unkonwn 0x56b181ac756554
9 unkonwn 0x26eb01ac97b9a8
10 unkonwn 0x73d081ac86a820
11 unkonwn 0x1c4d01ab7e1884
12 unkonwn 0xfc01ab7ee404
13 unkonwn 0x49f081ac7db250
14 unkonwn 0xce101ac84915c
15 unkonwn 0x7edd01abd40acc
16 unkonwn 0x2cad81abd40a8c
17 unkonwn 0x53ff01abd3ff30
18 unkonwn 0x661501abd3fbbc
19 unkonwn 0x5aeb81abcb6768
20 unkonwn 0x404001abd3f664
21 unkonwn 0x2ffd81ac73a7c4
22 unkonwn 0x476581d9099cd8
23 unkonwn 0x509081d89383c8
24 unkonwn 0x659f81d8938e5c
25 unkonwn 0x26c181d8937eb8
26 unkonwn 0x1ab581d893cea8
27 unkonwn 0x56c481d8c83904
28 unkonwn 0x563b81ae794c58
29 unkonwn 0x4dd581ab7e1884
30 unkonwn 0x4e4901ab7e4e5c
31 unkonwn 0x697381ae7d118c
32 unkonwn 0x1ca181ae7d0e08
33 unkonwn 0x204381ae7d1404
34 unkonwn 0x42a001abd62444
35 unkonwn 0x7ca881abd623c0
36 unkonwn 0x7e8c81abd61c7c
37 unkonwn 0x626881abd5c950
38 unkonwn 0x199c01abd5c254
39 unkonwn 0x209801adf9bd8c
40 unkonwn 0x458381d90a44c0
41 unkonwn 0x62f78102b5047c
42 libdyld.dylib 0x1ab818000 0x1ab818fd8

地址明显有问题,而导致翻译失败。

PAC

偶然间看到内网有人解决了最近某个版本的一个top 2的crash,提到了一个堆栈回溯失败的问题和原因,豁然开朗。

果然是PAC问题,虽然新iphone刚发布时就知道有PAC的特性,但我们一直没重视。而且本身我们的crash问题堆栈的量又特别特别少,导致我们也没认真对待。

正好结合最近和以前遇到的问题,发现问题系统设备都是arm64e设备和12系统,才意识到我们的堆栈回溯失败的根因也都是PAC问题,索性就再研究下了PAC。

什么是PAC

PAC即Pointer Authentication,它的目的即检测和保护地址不被意外或恶意修改,使得应用执行更加安全。

比如如果当前pc指向了PAC后的值0x00691e8104c560d4,而我们应用强制将其改为其他地址,则会导致内存访问错误或PAC校验失败而crash。

PAC特性是由硬件提供的,保护了函数调用期间,栈空间和地址的安全。

它利用VA的高位来实现,当执行该VA时,会有专门的指令解码VA并读取指令。任何导致该VA在读与写之间不一致的行为,都会触发PAC校验失败。PAC校验失败会触发内存崩溃而crash;

PAC的结构如下:

在iphone上,由于VA值的上限只用到36bits,所以会利用VA的高28位来作为PAC存储使用。

备注:xnu规定的iOS的VA最大值为 #define MACH_VM_MAX_ADDRESS 0x0000000FC0000000,即只需要36bits即可。

处理PAC

根据Apple的文档Preparing Your App to Work with Pointer Authentication ,处理PAC的地址问题我们直接使用ptrauth_strip宏(#include )即可,但该死的苹果限制了该宏仅在arm64e下才生效,所以此路不通。

但很庆幸,PAC的本质是在arm64e上它把VA无用的高28bits拿来用作PAC了,所以我们只要取低36bits即为实际的VA了。

备注:准确点是arm64e中PAC只用到了第62bit到36bit共27bits,最高位第63bit在arm64中是tag pointer标记位.

所以我们只要直接取低36bits就能愉快的玩耍了。

va = va & 0x0fffffffff;//得到PAC strip后的原始VA值.

结语

果然任何小问题都不要因为量不多而不重视它,很可能每个小问题的背后都隐藏了一个未知的新问题。

iOS正向开发果然越来越难了。


Copyright© 2013-2020

All Rights Reserved 京ICP备2023019179号-8