使用反汇编工具IDA查看动态库的汇编代码上下文,结合安卓系统生成的Tombstone文件,快速定位安卓app程序底层C++库的崩溃问题

本文介绍了如何结合IDA工具和Tombstone文件来定位安卓app中C++底层库的崩溃问题。通过查看Tombstone文件获取崩溃信息,使用IDA打开.so动态库文件,定位到崩溃的C++源代码行,分析空指针调用成员函数导致崩溃的原因。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

1、反汇编工具IDA工具介绍

2、产品及问题场景描述

3、查看安卓系统生成的Tombstone文件

4、使用IDA打开.so动态库文件,查看汇编代码的上下文,到C++源码中定位发生崩溃的那行代码

4.1、使用IDA打开.so动态库文件

4.2、切换到Text View文本视图模式

4.3、根据相对于函数的偏移,在汇编代码中找到对应位置,查看附近的汇编上下文

4.4、通过汇编代码上下文,找到对应的C++源代码位置

4.5、使用空指针去调用一个类成员函数,为啥会崩溃在函数调用处?

5、最后


VC++常用功能开发汇总(专栏文章列表,欢迎订阅,持续更新...)https://blog.youkuaiyun.com/chenlycly/article/details/124272585C++软件异常排查从入门到精通系列教程(专栏文章列表,欢迎订阅,持续更新...)https://blog.youkuaiyun.com/chenlycly/article/details/125529931C++软件分析工具从入门到精通案例集锦(专栏文章正在更新中...)https://blog.youkuaiyun.com/chenlycly/article/details/131405795C/C++基础与进阶(专栏文章,持续更新中...)https://blog.youkuaiyun.com/chenlycly/category_11931267.html       我们在排查软件异常时,有时可能需要用IDA反汇编工具去查看二进制文件的汇编代码,去辅助分析问题。最近在排查安卓系统中的app程序崩溃时,就使用了IDA工具,使用IDA打开.so动态库文件查看汇编代码上下文,结合安卓系统自动生成的Tombstone文件中的信息,最终找到了引发崩溃的原因。今天我们就来讲述一下这个问题的详细排查过程,以供大家借鉴或参考。

1、反汇编工具IDA介绍

       IDA是比利时Hex-Rays公司出品的一款强大的交互式静态反汇编工具。它可以直接反汇编出二进制文件的汇编代码,是目前软件逆向与安全分析领域最好用、最强大的一个静态反汇编软件,已成为众多软件安全分析人员不可缺少的利器!它支持Windows、Linux等多个平台,支持Intel X84、X64、ARM、MIPS等数十种CPU指令集。 IDA既支持打开Windows平台的.dll和.exe文件,也支持打开Linux平台的.so和.bin等二进制文件。

IDA是俄罗斯天才程序员llfak Guilfanov(尔法克-吉尔法诺威)开发的,llfak Guilfanov作为创始人兼首席执行官与2005年在比利时创建了Hex-Rays公司。

  • 可能有很多人不知道,俄罗斯在软件领域很牛,是个盛产天才程序员的国度,比如我们这里讲的强大静态反汇编工具IDA就是俄罗斯人llfak Guilfanov开发的!广受欢迎的IDE开发工具IDEA、PyChram和WebStorm所属公司Jetbrains公司是三个俄罗斯天才程序员创建的!在IT领域广泛使用的负载均衡与反向代理开源库Nginx以及大数据领域鼎鼎有名的ClickHouse开源数据库都是俄罗斯人开发的!
  • 俄罗斯人的逻辑思维很强,俄罗斯盛产数学家和化学家,比如伟大的数学家罗蒙诺索夫、切比雪夫,发明元素周期表的化学家门捷列夫!
  • 华为不少产品的命名与科学家名字有关,比如华为自研的数据库是以德国数学家高斯命名的(GaussDB),华为的Linux服务器操作系统是以瑞士数学家欧拉命名的(EulerOS)!

       在日常工作中,我们主要使用IDA查看二进制文件的汇编代码,去辅助排查C++软件异常问题。本文详细讲述一个使用IDA查看汇编代码快速定位软件崩溃的实例,详细讲解问题的完整分析过程,有一定的实战参考价值!

我们也可以使用IDA打开二进制文件去学习汇编代码:

  • 在学习汇编代码时,我们要将汇编代码与C++代码对应起来,这样才有意义。孤立地去学汇编,不能将汇编用到工作实战中,是没用的。我们在阅读汇编代码上下文时,一般是对照着C++代码去看的。
  • 我们可以在Visual Studio中Debug下调试C++代码时,转到反汇编页面查看C++代码对应的汇编代码。要查看Release版本的二进制文件的汇编代码,则需要使用IDA打开二进制文件查看。不过需要注意的是,Release下编译器会对代码进行优化,很难将汇编代码与C++代码完全对应起来。
  • 此外,使用IDA查看汇编代码时,最好将二进制文件的pdb文件取来,放到二进制文件的同级目录中,这样在IDA打开二进制文件时会通过读取pdb文件中函数及变量等符号,在解析出来的汇编代码中添加一些注释,通过这些注释信息,我们容易将汇编代码与C++代码对应起来,这对于阅读汇编代码上下文很有帮助。

2、产品及问题场景描述

       本文中出现问题的是一台嵌入式硬件设备,设备中使用的是安卓系统,设备运行的是我们的应用程序。应用程序的主体是用Java开发的安卓app程序,app程序通过C++实现的底层业务模块实现和远端服务器的通信交互。

app程序通过调用封装好的JNI接口去操作底层的C++模块,本案例中的崩溃是发生在底层的C++模块中。崩溃虽然发生在底层的C++模块中,但底层C++模块也是运行在app程序进程中的,所以直接导致了app程序的崩溃。

       app程序发生崩溃时,安卓系统感知到了,并生成了包含异常崩溃信息的Tombstone(墓碑)文件,这个文件类似于Linux系统中生成的CoreDump文件,我们在分析软件异常崩溃时,就是去分析这些文件。

       我们先是打开了Tombstone文件,查看了发生异常崩溃的具体异常类型以及崩溃时的函数调用堆栈,但通过Tombstone文件只知道崩溃发生在哪个函数中,函数中的代码篇幅比较长,无法定位崩溃发生在哪一行。

       于是乎,我们使用IDA打开函数调用堆栈中的显示的.so模块文件去查看汇编代码,查看发生崩溃的那条汇编代码附近的上下文,最终找到了发生崩溃的那行C++源码。

3、查看安卓系统生成的Tombstone文件

       从嵌入式设备中取来发生崩溃时安卓系统自动生成的Tombstone文件,打开该文件,看到了如下的信息:

首先,看到当前崩溃的原因为:null pointer dereference,空指针引用,即程序中使用了空指针引发了崩溃紧接着,看到发生崩溃时的函数调用堆栈,从堆栈上看,崩溃发生在libxxservice_hddll.so动态库中的CXXXServiceHMpHandler::OnTextImageCreateBannerInfoRsp函数中。调用堆栈中能看到具体的函数名,说明.so动态库文件中是有函数符号的。但看不到具体的代码行号,只能看到相对于函数CXXXServiceHMpHandler::OnTextImageCreateBannerInfoRsp的偏移:

#00 pc 0000000000075200  /xxxkyui/lib64/libxxservice_hddll.so (CXXXServiceHMpHandler::OnTextImageCreateBannerInfoRsp(mtmsg::CMtMsg*, unsigned int, unsigned int)+1048) (BuildId: d6e3064a3e1a03d9bea3c4496e78cb4942d187d1)

而CXXXServiceHMpHandler::OnTextImageCreateBannerInfoRsp函数中的代码篇幅比较长,我们没法确定崩溃发生在哪一行代码上。

当然如果问题是必现的,或者很好复现,则可以添加打印,将函数中用到的所有指针的值打印出来,崩溃后我们查看日志就能确定了。但有些问题是很难复现的,我们还是要多掌握一些分析手段才好!

       既然从函数调用堆栈中能看到具体的函数名及相对于函数(首地址)的偏移,我们可以使用IDA反汇编工具去查看函数所在模块libxxservice_hddll.so的汇编代码上下文,去辅助定位问题。

4、使用IDA打开.so动态库文件,查看汇编代码的上下文,到C++源码中定位发生崩溃的那行代码

4.1、使用IDA打开.so动态库文件

       我们的嵌入式硬件设备使用的安卓系统,主控CPU是基于ARM架构的,程序的代码也是在ARM平台的环境中编译的,IDA反汇编工具是支持ARM平台的,所以可以直接使用IDA打开程序的二进制文件查看汇编代码的。

4.1.1、需要使用64位IDA

       直接在Windows系统中启动IDA,会弹出如下的窗口:

点击New按钮去反汇编一个新的文件,然后弹出IDA的主窗口。然后直接将libxxservice_hddll.so文件拖入到IDA主窗口中打开,会提示使用64位的IDA打开该文件,如下:

libxxservice_hddll.so动态库是64位程序,所以要使用64位的IDA打开:

我们之前打开的是32位的IDA。

4.1.2、老版本IDA无法识别出.so动态库CPU架构类型,需要使用新版本IDA

       将libxxservice_hddll.so拖入到启动起来的64位IDA中弹出如下的提示框:

IDA将libxxservice_hddll.so文件类型识别为ELF64 for Unknown CPU [183] (Shared object) [elfldw]即IDA无法识别出编译文件的CPU平台类型,这有些奇怪,之前没遇到过。点击OK后,又弹出如下的提示框:

点击OK后打开的内容中看不到有效的汇编代码:

应该是当前的IDA没有识别出libxxservice_hddll.so所在平台的CPU类型(在对应类型CPU上编译出来的),所以没有生成有效的汇编代码

       经研究,可能是我们使用的IDA版本比较老导致的,我们当前使用的IDA版本比较老,版本位Version 6.1.110315,是2011年开发的:

而ARM64核心架构是ARM2012年才发布的,所以老版本IDA识别不出来,于是下载安装了较新的IDA 7.0(2017年开发的) 。

ARM64的历史

早在2007年,ARM公司已经开始了64位架构的研发;2011年ARM官方公布了第一套64位处理器架构“ARMv8”,并于当年11月签署了第一份授权协议。2012年10月,ARM公司发布了第一款基于64位架构的处理器核心“Cortex-A50”系列,该系列首批包括Cortex-A57和Cortex-A53两款型号,而这两款可以单独工作,也可以以big.LITTLE的形式协同工作。2015年2月,ARM公司又发布了Cortex-A57的升级型号Cortex-A72,性能再次提升接近1倍,同样可以与Cortex-A53搭配形成big.LITTLE双架构组合。

在ARM公司提出64位处理器战略之初,各方人士均预测ARM准备抢占Intel的服务器市场,但随着智能终端设备的高速发展,ARM64却成为了高性能智能设备的主流选择,包括苹果与绝大多数Android设备厂商。

将libxxservice_hddll.so文件拖入到IDA 7.0中就能识别出该文件的CPU架构类型了,如下:

打开后,就能看到完整的汇编代码了。

      关于老版本IDA无法识别基于ARM64机构CPU编译的.so动态库二进制文件的问题,我之前做了详细的记录与总结,感兴趣的朋友可以查看我的文章:

Relocations for this machine are not implemented,IDA版本过低导致打开二进制文件时生成汇编代码失败https://blog.youkuaiyun.com/chenlycly/article/details/135076536


       在这里,给大家重点推荐一下我的几个热门畅销专栏,欢迎订阅:(博客主页还有其他专栏,可以去查看)

专栏1:该专栏是核心精品专栏,当前订阅量已达到10000多个,专栏中包含大量项目实战分析案例,有很强的实战参考价值,广受好评!专栏文章持续更新中,已经更新到200篇以上!欢迎订阅!)

C++软件调试与异常排查从入门到精通系列文章汇总https://blog.youkuaiyun.com/chenlycly/article/details/125529931

本专栏根据多年C++软件异常排查的项目实践,系统地总结了引发C++软件异常的常见原因以及排查C++软件异常的常用思路与方法详细讲述了C++软件的调试方法与手段详细介绍分析C++软件问题的常用分析工具,以图文并茂的方式给出具体的项目问题实战分析实例(详细讲述分析排查过程,很有实战参考价值),带领大家逐步掌握C++软件调试与异常排查的相关技术,适合基础进阶和想做技术提升的相关C++开发人员!

考察一个开发人员的水平,一是看其编码及设计能力,二是要看其软件调试能力!所以软件调试能力(排查软件异常的能力)很重要,必须重视起来!能解决一般人解决不了的问题,既能提升个人能力及价值,也能体现对团队及公司的贡献!

专栏中的文章都是通过项目实战总结出来的,包含大量项目问题实战分析案例,有很强的实战参考价值!专栏文章还在持续更新中,预计文章篇数能更新到300篇以上!

专栏2:(本专栏涵盖了C++多方面的内容,是当前重点打造的专栏,订阅量已达8000多个,专栏文章已经更新到500多篇,持续更新中...)

C/C++实战进阶(专栏文章,持续更新中...)https://blog.youkuaiyun.com/chenlycly/category_11931267.html

以多年的开发实战为基础,总结并讲解一些的C/C++基础与项目实战进阶内容,以图文并茂的方式对相关知识点进行详细地展开与阐述!专栏涉及了C/C++领域多个方面的内容,包括C++基础及编程要点(模版泛型编程、STL容器及算法函数的使用等)、数据结构与算法C++11及以上新特性(开源代码中可能会用到很多新特性(比如WebRTC开源库),日常编码中也会用到部分新特性,面试时也会频繁地涉及到,学习新特性很有必要)、常用C++开源库的介绍与使用(比如SQLite、libcurl、libwebsockets、libevent、jsoncpp/RapidJson、Redis、RabbitMQ、MongoDB、MQTT、ZooKeeper、OpenCV、FFmpeg、SDL、GStreamer、Live555、ReactOS等)、代码分享(调用系统API、使用开源库)、常用编程技术(动态库、多线程、多进程、数据库及网络编程等)、软件UI编程(Win32/duilib/QT/MFC)、C++软件调试技术(引发C++软件异常的常见原因分析与总结、排查C++软件异常的手段与方法、分析C++软件异常的基础知识、使用常用软件分析工具分析C++软件问题、多个项目实战问题分析案例分享等)、设计模式(单例模式、工厂模式、观察者模式、状态模式等)、网络基础知识与网络问题分析进阶内容(实战问题分析实例分享)等。本专栏的内容都是建立在项目实践的基础上,来源于项目实战,服务于项目实战,很有实战参考价值!

专栏3:  

C++常用软件分析工具从入门到精通案例集锦汇总(专栏文章,持续更新中...)https://blog.youkuaiyun.com/chenlycly/article/details/131405795

常用的C++软件辅助分析工具有SPY++、PE工具、Dependency Walker、GDIView、Process Explorer、Process Monitor、API Monitor、Clumsy、Windbg、IDA Pro等,本专栏详细介绍如何使用这些工具去巧妙地分析和解决日常工作中遇到的问题,很有实战参考价值!

专栏4:   

VC++常用功能开发汇总(专栏文章,持续更新中...)https://blog.youkuaiyun.com/chenlycly/article/details/124272585

将10多年C++开发实践中常用的功能,以高质量的代码展现出来。这些常用的高质量规范代码,可以直接拿到项目中使用,能有效地解决软件开发过程中遇到的问题。

专栏5: 

C++ 软件开发从入门到精通(专栏文章,持续更新中...)https://blog.youkuaiyun.com/chenlycly/category_12695902.html

根据多年C++软件开发实践,详细地总结了C/C++软件开发相关技术实现细节,分享了大量的实战案例,很有实战参考价值。


4.2、切换到Text View文本视图模式

        打开二进制文件后,会默认显示Graphic View视图模式:

 要右键单击,在弹出的右键菜单中单击“Text View”菜单项,切换到文本模式页面,如下:

4.3、根据相对于函数的偏移,在汇编代码中找到对应位置,查看附近的汇编上下文

        首先,我们需要找到在IDA显示的汇编代码中找到CXXXServiceHMpHandler::OnTextImageCreateBannerInfoRsp函数的位置。在菜单栏中中点击Jump -> Jump to function...,打开如下的窗口:

点击窗口下方的Search按钮,在弹出的窗口中输入函数名OnTextImageCreateBannerInfoRsp:

然后点击OK按钮,在函数列表中搜索到该函数,双击之直接跳转到该函数的汇编代码中,如下所示:

可以看到该函数的函数地址(函数首地址)为0x0000000000074DE8,根据Tombstone文件中显示的相对函数的偏移:

#00 pc 0000000000075200  /xxxkyui/lib64/libxxservice_hddll.so (CXXXServiceHMpHandler::OnTextImageCreateBannerInfoRsp(mtmsg::CMtMsg*, unsigned int, unsigned int)+1048) (BuildId: d6e3064a3e1a03d9bea3c4496e78cb4942d187d1)

计算新的地址:

0x0000000000074DE8 + 0x418(对应于10进制的1048)=  0x0000000000075200

然后在IDA中搜索该地址0x0000000000075200,找到对应的汇编代码行。具体的做法是,将鼠标点进汇编代码窗口中(使该窗口获得焦点),然后按下快捷键g,弹出Jump to address窗口,输入上面计算出来的地址0x0000000000075200:

点击OK,就会跳转到对应的行,如下所示:

4.4、通过汇编代码上下文,找到对应的C++源代码位置

       我们平时看惯了X86平台的汇编代码,看这个ARM架构的汇编代码很不习惯,无论是汇编指令的名称,还是寄存器的名称,都有很大的差异。感觉还是X86平台的汇编代码好阅读一些。

       上面我们在汇编代码中定位到了位置,但与汇编代码对应的C++源码是哪一行呢?此外,Release下编译时编译器会对C++代码进行优化(有些变量或函数调用可能会被优化掉),导致汇编代码和C++代码是不完全一致。

       该怎么将汇编代码与C++源码对应起来呢?难道我们要一句一句汇编代码去啃?强行去阅读汇编代码上下文,是需要有一定的汇编功底的,一般人比较难做到。一般我们借助汇编上下文中的注释信息去辅助阅读,本例中我们就是使用注释信息快读定位的。

一般在阅读汇编代码上下文时,一方面借助汇编代码中的注释,另一方面将汇编代码与C++源码对照着看!

       0x0000000000075200地址对应的汇编代码行,该行代码下面紧接就看到注释,是常量值字符串的注释:

但看不到完整的字符串。这个地方有个技巧,可以将鼠标移动到变量上,就会以TooTip的方式显示变量中的完整内容,如下所示:

这个地方巧了,这样的字符串是打印日志中的,于是到C++源码中找到CXXXServiceHMpHandler::OnTextImageCreateBannerInfoRsp函数,在函数中找“[CXXXServiceHMpHandler::OnTextImageCreateBannerInfoRsp] dispatch”这样的打印,确实有这行打印,如下所示:

所以就找到了0x0000000000075200地址对应的C++源码大概的行了,所以本例中的空指针问题应该就是上图中的ptTip指针,即该指针值为空,结果使用该指针调用value接口产生了崩溃。

       关于IDA工具介绍、详细使用说明以及如何使用IDA去辅助排查软件异常,可以参见我之前写的文章:

IDA反汇编工具使用详解https://blog.youkuaiyun.com/chenlycly/article/details/120635120使用IDA查看汇编代码上下文去辅助排查C++软件异常问题https://blog.youkuaiyun.com/chenlycly/article/details/128942626使用反汇编工具IDA查看发生异常的汇编代码的上下文去辅助分析C++软件异常https://blog.youkuaiyun.com/chenlycly/article/details/132158574

       关于C++程序员为什么要学习汇编、学习汇编有哪些好处以及如何学习汇编,可以查看我之前写的文章:
C/C++程序员为什么要了解汇编?了解汇编有哪些好处?如何学习汇编?https://blog.youkuaiyun.com/chenlycly/article/details/142795872

4.5、使用空指针去调用一个类成员函数,为啥会崩溃在函数调用处?

       为啥崩溃在函数调用处呢?如果调用的函数是个普通函数(非虚函数),函数调用处一般不会崩溃的,如果函数内部访问了所在类的成员变量,则使用空指针调用会崩溃在被调用函数内部,而不是崩溃call函数时!

       有一种可能,被调用函数是虚函数,对于虚函数的调用,需要通过二次寻址到虚函数表中找到虚函数的地址(虚函数代码段地址,此处需要区分一下代码段地址与数据段地址),在二次寻址的过程中将空指针作为内存地址去访问内存,就触发了内存访问违例,引发崩溃。

       还有一种情况,被调用函数中定义了占用很大栈内存的局部变量(比如使用一个定义很大的结构体去定义了一个局部变量),导致当前线程栈溢出(当前线程占用的栈空间达到了线程创建时分配的栈空间的上限)。对于这种线程栈溢出的场景,可能就会崩溃在函数调用的地方,这类问题我们在项目中遇到过。关于栈溢出引发异常的项目实战问题实例,可以查看我的文章:

Stack overflow 线程栈溢出异常,程序崩溃在汇编代码 test dword ptr [eax],eax 上的问题排查https://blog.youkuaiyun.com/chenlycly/article/details/131743305

5、最后

       本文详细讲述了如何将Tombstone文件和IDA反汇编工具结合起来快速定位崩溃的完整过程,希望能给大家提供一定的借鉴与参考。此外,通过该问题详细讲解了如何使用IDA反汇编工具。

评论 112
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

dvlinker

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值