前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >如何符号化Objective-C调用栈如何符号化Objective-C调用栈

如何符号化Objective-C调用栈如何符号化Objective-C调用栈

作者头像
且行且珍惜_iOS
发布2021-09-07 16:43:42
7630
发布2021-09-07 16:43:42
举报

本文讲述的是符号化“残破”的栈,如果你有一个系统生成的crash日志,请交给Xcode自带的symbolicatecrash脚本。

Symbolicatecrash脚本的核心也是通过atos功能逐行符号化,但人家封装好了,比自己手动一行一行做快很多。

示例栈:

代码语言:javascript
复制
    0   XSQSymbolicateDemo                  0x00000001000ba530 XSQSymbolicateDemo + 25904
    1   XSQSymbolicateDemo                  0x00000001000ba4f0 XSQSymbolicateDemo + 25840
    2   XSQSymbolicateDemo                  0x00000001000ba4bc XSQSymbolicateDemo + 25788
    3   XSQSymbolicateDemo                  0x00000001000ba478 XSQSymbolicateDemo + 25720
    4   UIKit                               0x00000001966870ec <redacted> + 96
    5   UIKit                               0x000000019668706c <redacted> + 80
    6   UIKit                               0x00000001966715e0 <redacted> + 440
    7   UIKit                               0x0000000196686950 <redacted> + 576
    8   UIKit                               0x000000019668646c <redacted> + 2480
    9   UIKit                               0x0000000196681804 <redacted> + 3192
    10  UIKit                               0x0000000196652418 <redacted> + 340
    11  UIKit                               0x0000000196e4bf64 <redacted> + 2400

这是我写的一个demo app,并且在编译后期滤去了符号表,所以仅能看到一些奇怪的地址。

如何符号化第三方app内的符号

以第一行:

代码语言:javascript
复制
0   XSQSymbolicateDemo                  0x00000001000ba530 XSQSymbolicateDemo + 25904

为例

需要条件:

(1)atos工具(Xcode安装时一般会自带)

(2)确认app运行的架构(armv7、arm64)

(3)app对应的dSYM文件(出包时获得)

(4)app代码载入到内存的基地址(后文详细介绍)

方法:

在命令行中输入:

代码语言:javascript
复制
xcrun atos -arch arm64 -o ./XSQSymbolicateDemo.app.dSYM/Contents/Resources/DWARF/XSQSymbolicateDemo  -l  0x1000b4000 0x00000001000ba530

即可得到符号化后的结果:

代码语言:javascript
复制
-[ViewController helloWorld2] (in XSQSymbolicateDemo) (ViewController.m:100)
如何符号化系统动态库中的符号

以这一行为例:

代码语言:javascript
复制
4   UIKit                               0x00000001966870ec <redacted> + 96

需要条件:

(1)atos工具(Xcode安装时一般会自带)

(2)确认app运行的架构(armv7、arm64)

(2)该OS版本、该动态库的符号文件(将该手机连接到电脑的Xcode上,会自动同步系统符号文件)

(3)该动态库载入到内存的基地址(后文详细介绍)

方法:

在命令行中输入:

代码语言:javascript
复制
xcrun atos -arch arm64 -o ~/Library/Developer/Xcode/iOS\ DeviceSupport/10.3.1\ \(14E304\)/Symbols/System/Library/Frameworks/UIKit.framework/UIKit -l  0x196642000 0x00000001966870ec
-[UIApplication sendAction:to:from:forEvent:] (in UIKit) + 96

即可得到符号化后的结果:

代码语言:javascript
复制
-[UIApplication sendAction:to:from:forEvent:] (in UIKit) + 96
如何获取基地址

注意:基地址在进程每次启动时决定,所以重启进程后,符号化时必须使用当次启动的基地址

方案一:从iOS生成的crash日志中获取

在iOS系统生成的crash日志中的下半部分,有这样的一些信息:

蓝色框圈出来的部分,即为app代码载入到内存的基地址

红色框圈出来的部分,即为各个动态库载入到内存的基地址

方案二:在app运行时打印

可以在app中调用如下代码获取各个image的基地址:

代码语言:javascript
复制
void printAllImage()
{
    for (int i = 0; i < _dyld_image_count(); i++) {
        char *image_name = (char *)_dyld_get_image_name(i);
        const struct mach_header *mh = _dyld_get_image_header(i);
        intptr_t vmaddr_slide = _dyld_get_image_vmaddr_slide(i);
        
        NSLog(@"Image name %s at address 0x%llx and ASLR slide 0x%lx.\n",
               image_name, (mach_vm_address_t)mh, vmaddr_slide);
    }
}

得到如下输出:

代码语言:javascript
复制
Image name /var/containers/Bundle/Application/351121C8-CFE4-49AD-ACC0-A70C5BF1C4A6/XSQSymbolicateDemo.app/XSQSymbolicateDemo at address 0x1000b4000 and ASLR slide 0xb4000.
 Image name /System/Library/Frameworks/Foundation.framework/Foundation at address 0x190f0c000 and ASLR slide 0xeedc000.
 Image name /usr/lib/libarchive.2.dylib at address 0x190ee0000 and ASLR slide 0xeedc000.
 Image name /usr/lib/libbz2.1.0.dylib at address 0x190e9e000 and ASLR slide 0xeedc000.
 Image name /usr/lib/libSystem.B.dylib at address 0x18ef04000 and ASLR slide 0xeedc000.
 Image name /usr/lib/system/libcache.dylib at address 0x18f35a000 and ASLR slide 0xeedc000.
......

可以看到第一行代表的是app自身,之后的每一行是app载入的动态库们。

介绍加载和ASLR

大致理解:

在进程启动的时候,内核加载器或者dyld会将指令加载到内存中。

ASLR全名Address Space Layout Randomization,地址空间布局随机化,用于防范恶意程序对已知地址进行攻击

在ASLR引入之前,由于加载的规则是固定的,所以理论上,一个进程不管重启多少次,每条指令对应的内存中的地址都是一样的。而每条指令对应到内存中的哪个地址,可以通过分析Mach-O文件分析出来。这就容易产生安全漏洞。

ASLR引入后,在进程启动前期的加载阶段,会生成一个随机数offset,让加载形成的内存整体偏移一个offset。

这样一个进程多次启动,每次行程的内存空间布局都不完全一致。同一个指令,经过多次启动,每次都会被布局到一个新计算出来的地址。

所以仅仅凭借“一个指令在内存中的地址”和dSYM文件,是无法进行符号化的,因为这个“地址”同时依赖于ASLR生成的offset。

我理解其实只需要一个offset,配合已知的架构、加载方式等信息,应该就能推测出app自身的基地址和各个库的基地址。尝试后也证明,各个库的基地址-offset后的值在同个设备的多次启动上是一致的。 但是为了图省事,还是自己打印一下所有库的基地址吧(´・ω・`)

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2017-9-1 21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 如何符号化第三方app内的符号
  • 如何符号化系统动态库中的符号
  • 如何获取基地址
  • 介绍加载和ASLR
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档