热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

iOS详解没有dSYM文件如何解析iOS崩溃日志

Xcode支持崩溃日志自动符号化,前提是本地有当时BuildArchive生成的dSYM文件,iOS崩溃日志符号化后,可以帮助开发者更好的

Xcode支持崩溃日志自动符号化,前提是本地有当时Build/Archive生成的dSYM文件,iOS崩溃日志符号化后,可以帮助开发者更好的定位问题,但如果dSYM文件丢失或拿到的崩溃日志不是标准的crash log,如何定位crash呢,笔者结过尝试发现一样可以定位到具体函数。

  在无dSYM文件情况下,之所以无法解析出崩溃地址对应的函数名,是因为Xcode在导出ipa时会去除Symbol Table(符号表)的非系统符号部分。这时address无法对应函数名,所以无法确定是在哪个函数或block中出了问题。因此解析日志的关键是要恢复符号表,国内已有大神做过研究 杨君的小黑屋,本文基于此完成解析目标。

我们以测试程序CrashTest的崩溃为例,介绍一下具体解析步骤

如图,

 

 

我们拿到了崩溃日志,这是一个arm64架构的崩溃日志,从最后的backtrace我们知道程序在访问数组元素时异常终止,但由于本地没有对应的dSYM文件,Xcode没有将红框内3,4两行符号化为具体的函数名,本文的工作就是要将这2行符号化

开始之前,先解释一下这几行地址的含义

:左边这一列是崩溃时的调用栈地址(虚拟内存地址)

基址:基址指向的地址是CrashTest这个模块加载到内存中的起始地址

偏移:左边栈地址 = 基址 + 偏移 (注意是10进制的)

 


什么?你拿到的崩溃日志不是这样的! (老司机请绕过)

像这样,只有一堆地址


Exception Type: EXC_CRASH (SIGABRT)
Exception Codes: 0x0000000000000000, 0x0000000000000000
Triggered by Thread: 0Last Exception Backtrace:
(0x186e9de48 0x1975dc0e4 0x186d83a54 0x10000c00c 0x10000bf7c 0x18b6810f8 0x18b66a22c 0x18b680a94 0x18b680720 0x18b679c74 0x18b64d38c 0x18b8ec1b4 0x18b64b8f4 0x186e560e8 0x186e5538c 0x186e5343c 0x186d811f4 0x18ff0f5a4 0x18b6b2784 0x10000c574 0x197c4aa08)Thread 0 name: Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0 libsystem_kernel.dylib 0x0000000197d63270 0x197d48000 + 111216
1 libsystem_pthread.dylib 0x0000000197e01224 0x197dfc000 + 21028
2 libsystem_c.dylib 0x0000000197cdab14 0x197c78000 + 404244
3 libc++abi.dylib 0x0000000196dad414 0x196dac000 + 5140
4 libc++abi.dylib 0x0000000196dccb88 0x196dac000 + 134024
5 libobjc.A.dylib 0x00000001975dc3bc 0x1975d4000 + 33724
6 libc++abi.dylib 0x0000000196dc9bb0 0x196dac000 + 121776
7 libc++abi.dylib 0x0000000196dc9738 0x196dac000 + 120632
8 libobjc.A.dylib 0x00000001975dc290 0x1975d4000 + 33424
9 CoreFoundation 0x0000000186d812a0 0x186d78000 + 37536
10 GraphicsServices 0x000000018ff0f5a0 0x18ff04000 + 46496
11 UIKit 0x000000018b6b2780 0x18b63c000 + 485248
12 CrashTest 0x000000010000c570 0x100004000 + 34160
13 libdyld.dylib 0x0000000197c4aa04 0x197c48000 + 10756


 没有关系,其实和上面是一样的,我们来找找基址和偏移

往下找到 Binary Images段,这里显示的就是崩溃程序当时加载的所有库的快照


Binary Images:
0x100004000 - 0x10000ffff CrashTest arm64 <5fc8820b297631d087e5e665b261ed0c> /var/mobile/Containers/Bundle/Application/D8F09771-5B65-4403-A19C-CE77DAF32623/CrashTest.app/CrashTest // 这里第一行便是我们要找的
0x120070000 - 0x120097fff dyld arm64 /usr/lib/dyld
0x185678000 - 0x18580bfff AVFoundation arm64 <0c542593e3613f82b7e860cb5beeeed6> /System/Library/Frameworks/AVFoundation.framework/AVFoundation
0x18580c000 - 0x185870fff libAVFAudio.dylib arm64 /System/Library/Frameworks/AVFoundation.framework/libAVFAudio.dylib
0x1858b4000 - 0x1858b4fff Accelerate arm64 /System/Library/Frameworks/Accelerate.framework/Accelerate
0x1858cc000 - 0x185aebfff vImage arm64 /System/Library/Frameworks/Accelerate.framework/Frameworks/vImage.framework/vImage
0x185aec000 - 0x185b93fff libBLAS.dylib arm64 /System/Library/Frameworks/Accelerate.framework/Frameworks/vecLib.framework/libBLAS.dylib


 然后找到我们的应用 CrashTest


0x100004000 - 0x10000ffff CrashTest arm64 <5fc8820b297631d087e5e665b261ed0c> /var/mobile/Containers/Bundle/Application/D8F09771-5B65-4403-A19C-CE77DAF32623/CrashTest.app/CrashTest

 我们看到行首有个地址区间 0x100004000 - 0x10000ffff &#xff0c; 这便是崩溃程序的内存区&#xff0c;起始地址&#xff08;基址&#xff09;为 0x100004000&#xff0c;OK基址找到了。

然后我们再看看崩溃时的栈


Last Exception Backtrace:
(0x186e9de48 0x1975dc0e4 0x186d83a54 0x10000c00c 0x10000bf7c 0x18b6810f8 0x18b66a22c 0x18b680a94 0x18b680720 0x18b679c74 0x18b64d38c 0x18b8ec1b4 0x18b64b8f4 0x186e560e8 0x186e5538c 0x186e5343c 0x186d811f4 0x18ff0f5a4 0x18b6b2784 0x10000c574 0x197c4aa08)

 其中位于地址区间 0x100004000 - 0x10000ffff 的&#xff0c;有2个 0x10000c00c 0x10000bf7c&#xff0c;这就是崩溃程序的调用栈&#xff0c;其余地址为系统库函数调用栈。

 


OK开始动手


1. 下载符号恢复工具

修改权限


chmod a&#43;x restore-symbol

 


2. 恢复符号

我们拿到的崩溃日志来自arm64机器&#xff0c;所以先将二进制文件 CrashTest.app/CrashTest 瘦身 &#xff08;必须正确选择目标CPU架构类型&#xff0c;否则解析出来也是错的&#xff09;


lipo -thin arm64 CrashTest -output CrashTest-arm64

 接着用工具恢复符号表


./restore-symbol -o CrashTest-symbol CrashTest-arm64

 现在我们得到了一个恢复了符号表的二时制文件 CrashTest-symbol


3. 使用苹果自带命令行工具atos&#xff0c;将崩溃地址解析成具体函数


atos -arch arm64 -o CrashTest-symbol -l 0x100030000 0x100034340 0x1000342b0
# 简单解释一下这个命令&#xff0c;atos -arch CPU架构 -o 进制文件 -l 起始地址 ...一系列内存地址
# -l 后面跟的是模块的起始地址&#xff0c;再后面可以罗列很多地址&#xff0c;该命令会依次解析出具体函数

得到如下输出


-[ViewController getChild:] (in CrashTest-symbol) &#43; 64
-[ViewController crashOnFunc:] (in CrashTest-symbol) &#43; 44

至此&#xff0c;完成了解析。

本篇涉及的崩溃是普通函数中的崩溃&#xff0c;如果崩溃发生在block中&#xff0c;则需要借住反编译工具&#xff0c;请参考 恢复二进制文件中的block符号表

 PS&#xff1a;再多说一点&#xff0c;上面的解析输出我们看到在函数后面有一个 &#43; 64&#xff0c; 这个&#43; 64、&#43; 44是什么意思呢&#xff0c;我开始也不太明白&#xff0c;仔细观察Hopper/IDA解析出来的的汇编代码&#xff0c;才明白原来这也是个偏移&#xff0c;是指调用地址相对于函数的起始地址的偏移&#xff0c;并非.m文件中的代码行数。

附件&#xff1a;

本篇使用的DEMO

参考 & 感谢

  杨君的小黑屋 http://blog.imjun.net/posts/restore-symbol-of-iOS-app/

  


推荐阅读
  • 本文介绍了C#中数据集DataSet对象的使用及相关方法详解,包括DataSet对象的概述、与数据关系对象的互联、Rows集合和Columns集合的组成,以及DataSet对象常用的方法之一——Merge方法的使用。通过本文的阅读,读者可以了解到DataSet对象在C#中的重要性和使用方法。 ... [详细]
  • 深入理解Kafka服务端请求队列中请求的处理
    本文深入分析了Kafka服务端请求队列中请求的处理过程,详细介绍了请求的封装和放入请求队列的过程,以及处理请求的线程池的创建和容量设置。通过场景分析、图示说明和源码分析,帮助读者更好地理解Kafka服务端的工作原理。 ... [详细]
  • Webpack5内置处理图片资源的配置方法
    本文介绍了在Webpack5中处理图片资源的配置方法。在Webpack4中,我们需要使用file-loader和url-loader来处理图片资源,但是在Webpack5中,这两个Loader的功能已经被内置到Webpack中,我们只需要简单配置即可实现图片资源的处理。本文还介绍了一些常用的配置方法,如匹配不同类型的图片文件、设置输出路径等。通过本文的学习,读者可以快速掌握Webpack5处理图片资源的方法。 ... [详细]
  • 开发笔记:计网局域网:NAT 是如何工作的?
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了计网-局域网:NAT是如何工作的?相关的知识,希望对你有一定的参考价值。 ... [详细]
  • CentOS 7部署KVM虚拟化环境之一架构介绍
    本文介绍了CentOS 7部署KVM虚拟化环境的架构,详细解释了虚拟化技术的概念和原理,包括全虚拟化和半虚拟化。同时介绍了虚拟机的概念和虚拟化软件的作用。 ... [详细]
  • 本文介绍了在Android开发中使用软引用和弱引用的应用。如果一个对象只具有软引用,那么只有在内存不够的情况下才会被回收,可以用来实现内存敏感的高速缓存;而如果一个对象只具有弱引用,不管内存是否足够,都会被垃圾回收器回收。软引用和弱引用还可以与引用队列联合使用,当被引用的对象被回收时,会将引用加入到关联的引用队列中。软引用和弱引用的根本区别在于生命周期的长短,弱引用的对象可能随时被回收,而软引用的对象只有在内存不够时才会被回收。 ... [详细]
  • STL迭代器的种类及其功能介绍
    本文介绍了标准模板库(STL)定义的五种迭代器的种类和功能。通过图表展示了这几种迭代器之间的关系,并详细描述了各个迭代器的功能和使用方法。其中,输入迭代器用于从容器中读取元素,输出迭代器用于向容器中写入元素,正向迭代器是输入迭代器和输出迭代器的组合。本文的目的是帮助读者更好地理解STL迭代器的使用方法和特点。 ... [详细]
  • 深入解析Linux下的I/O多路转接epoll技术
    本文深入解析了Linux下的I/O多路转接epoll技术,介绍了select和poll函数的问题,以及epoll函数的设计和优点。同时讲解了epoll函数的使用方法,包括epoll_create和epoll_ctl两个系统调用。 ... [详细]
  • 本文介绍了SPOJ2829题目的解法及优化方法。题目要求找出满足一定条件的数列,并对结果取模。文章详细解释了解题思路和算法实现,并提出了使用FMT优化的方法。最后,对于第三个限制条件,作者给出了处理方法。文章最后给出了代码实现。 ... [详细]
  • 广度优先遍历(BFS)算法的概述、代码实现和应用
    本文介绍了广度优先遍历(BFS)算法的概述、邻接矩阵和邻接表的代码实现,并讨论了BFS在求解最短路径或最短步数问题上的应用。以LeetCode中的934.最短的桥为例,详细阐述了BFS的具体思路和代码实现。最后,推荐了一些相关的BFS算法题目供大家练习。 ... [详细]
  • Annotation的大材小用
    为什么80%的码农都做不了架构师?最近在开发一些通用的excel数据导入的功能,由于涉及到导入的模块很多,所以开发了一个比较通用的e ... [详细]
  • C++ STL复习(13)容器适配器
    STL提供了3种容器适配器,分别为stack栈适配器、queue队列适配器以及priority_queue优先权队列适配器。不同场景下,由于不同的序列式 ... [详细]
  • QuestionThereareatotalofncoursesyouhavetotake,labeledfrom0ton-1.Somecoursesmayhaveprerequi ... [详细]
  • RingBuffer,或者说CircularBuffer,是一个长度固定的缓冲区,当从一端插入元素超过指定的最大长度时,缓冲区另一端的元素 ... [详细]
  • http:oj.leetcode.comproblemsminimum-depth-of-binary-tree贡献了一次runtimeerror,因为如果输入为{}即空的时候,出 ... [详细]
author-avatar
vbppn65853
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有