揭秘Android Tombstone:崩溃位置的秘密研究-Crash Location

由于一些工作原因,最近对Android系统发生crash的Tombstone展开了一定的研究。

这里我谈一下关于对于Android Libstagefright 整数溢出漏洞的crash Tombstone的研究。看一下在包含整数溢出功能的MP4文件从PC传输进Android的时候造成的Tombstone0_0。

1、研究头部信息

Build fingerprint: 'Android/aosp_bullhead/bullhead:6.0.1/MTC19V/hypo08180946:userdebug/test-keys'
Revision: 'rev_1.0'
ABI: 'arm'
pid: 472, tid: 910, name: Binder_3  >>> /system/bin/mediaserver <<<
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x41d00014

从中我们可以看到如下信息:

  1. Android版本是6.01_MTC19V
  2. 手机版本是bullhead
  3. 系统是userdebug模式
  4. 底层框架是arm
  5. 出错的文件夹位于mediaserver
  6. 错误内容是SIGSEGV(无效内存引用)和SEGV_MAPPERR(尝试去写只读内存)
  7. 错误时的地址是0x41d00014

距离发现问题只有一步之遥。但直到这一步,你固然可以确定Android版本,手机版本,底层框架,出错的大致文件夹,错误类型和错误地址。但你拥有了AOSP源码后,你不会认为这些信息就足够找到错误,甚至在茫茫代码海中,连定位到一个比较准确的出错cpp都做不到。但是万事都得慢慢来,所以在这一步后,我们建立起初步的用来追寻root cause的基础——AOSP的源代码和Add2Line_arm的调试工具。(这一步如果不会,还是先补下基础)

首先通过很多的文献阅读我们确定了一个观念:

  1. 距离crash最近的.so文件触发了这个crash。
  2. 真正的root cause可能并不是触发这个crash的元凶,而隐藏在crash路径链中。
  3. 相同的crash路径应该相同。

但是由于这里的多线程原因,经过各种观察,我们稍作更改这个结论:

  1. 距离crash最近的.so文件触发了这个crash,触发crash是由于一些特定的操作。
  2. 真正的root cause并不一定在crash线程中,也可能存在于其他的线程中。
  3. 多线程导致crash的路径不尽相同,但必然高度相似。

2、接下来看backtrace

backtrace:
    #00 pc 0000e602  /system/lib/libutils.so (_ZN7android7RefBase12weakref_type7incWeakEPKv+5)
    #01 pc 0000e619  /system/lib/libutils.so (_ZNK7android7RefBase9incStrongEPKv+6)
    #02 pc 000160b7  /system/lib/libaudiopolicymanagerdefault.so (_ZN7android2spINS_16EffectDescriptorEEC2ERKS2_+12)
    #03 pc 00016fe1  /system/lib/libaudiopolicymanagerdefault.so (_ZN7android18AudioPolicyManager20getDeviceForStrategyENS_16routing_strategyEb+34)
    #04 pc 00019721  /system/lib/libaudiopolicymanagerdefault.so (_ZN7android18AudioPolicyManager9getOutputE19audio_stream_type_tj14audio_format_tj20audio_output_flags_tPK20audio_offload_info_t+28)
    #05 pc 00008e6b  /system/lib/libaudiopolicyservice.so
    #06 pc 0008aaa1  /system/lib/libmedia.so (_ZN7android20BnAudioPolicyService10onTransactEjRKNS_6ParcelEPS1_j+1168)
    #07 pc 000199c9  /system/lib/libbinder.so (_ZN7android7BBinder8transactEjRKNS_6ParcelEPS1_j+60)
    #08 pc 0001ed53  /system/lib/libbinder.so (_ZN7android14IPCThreadState14executeCommandEi+550)
    #09 pc 0001eebd  /system/lib/libbinder.so (_ZN7android14IPCThreadState20getAndExecuteCommandEv+64)
    #10 pc 0001ef21  /system/lib/libbinder.so (_ZN7android14IPCThreadState14joinThreadPoolEb+48)
    #11 pc 000239c1  /system/lib/libbinder.so
    #12 pc 00010071  /system/lib/libutils.so (_ZN7android6Thread11_threadLoopEPv+112)
    #13 pc 0003f883  /system/lib/libc.so (_ZL15__pthread_startPv+30)
    #14 pc 00019f75  /system/lib/libc.so (__start_thread+6)

我们得知道Tombstone记录帧的时候是倒着记录的,#00 pc是crash的触发位置。用add2line看一下源代码——const int32t c = android_atomic_inc(&ref->mStrong)。

完美满足了错误记录中说到的内存越界,因为这样一个原子操作,从其本身角度出错的概率极低,那么说明极大的概率是ref->mStrong这个内存地址非法,从而对一些只读区域试图进行一些写操作。追溯源代码,可以看到ref->incweak(id)调用了这个函数。正好巧的是#01 pc对应的代码就是ref->incweak(id),说明逻辑正确。同时猜测可以更改的mRefs是问题根源,因为类似于incweak/addStrong这些函数由于其太普遍,如果其本身具有错误则在各种位置都会报错,那么从概率的角度来说,mRefs被更改,或者本身出错的可能远远大于这些基础操作的可能。

同时我们留意到了*id的来源,是looper->incStrong(void) ALoop::threadDestructor。这就涉及到了多线程的问题,更深入进去其实可以知道这是对每个不同线程id进行的一些StrongPoint的更改。这里不做多研究。然后对更多的pc进行解析。

#02 pc <sp>... mptr->incStrong
#03 pc AudioPolicyManager->getDeviceForStrategy()
#04 pc AudioPolicyManager->Device getoutput()
#05 pc AudioService->getoutput()
#06 pc libmedia.so  BnAudioPolicyService::OnTransact——————rely->writeInt32...

后续的pc并无太大意义,大致是Thread多线程和Blinder从Java层进入Native层。根据这些pc记录,我们可以大致构建出一个路径:

首先多线程创建——Java层到Native层的Flinger——libmedia进行onTransact通信——AudioPolicyService服务启动获取外部信息——AudioPolicyManager启动使设备获得输入——StrongPoint出错,造成了非法的内存写。

根据我们的理论我们也查看了其他的多线程,由于没有太多的内容,这里略过。同时我们根本无法确定真正的root cause出自哪里,但是从理论1来说,在AudioPolicyManager启动getoutput()后到StrongPoint出错前这块区域出错的概率最大。因为这里最大的变量是——读入了一个外部输入,虽然我们目前并不知道读入了一个什么东西,但是可以知道他一定是一个media的内容。

至此PC记录已经再无内容可以获得。
那么接下来不急着先看源代码,可以先看Main log

那么首先肯定最关注crash距离最近的一些log

06-18 01:47:01.034   472   472 E MPEG4Extractor: No width or height, assuming worst case 1080p
06-18 01:47:01.034   472   472 E MPEG4Extractor: [myselfdebug] timescale skip
06-18 01:47:01.057   472   472 I SampleTable: [myselfdebug] New change
06-18 01:47:01.058   472   472 I SampleTable: [myselfdebug] SampleToChunkEntry = 0xf62d6988
06-18 01:47:01.058   472   472 I SampleTable: [myselfdebug] mDataSource = 0xf62d6908

除开我自己设计的一些【myselfdebug】,可以看到最后一条 E MPEG4Extractor: No width or height, assuming worst case 1080p。很明显这个是libstagefright.so中的一个Error报错,我们终于可以知道这个错误的输入是什么了——是一个MP4的文件,并且其格式没有涉及width和height,这非常不同寻常。

而且从这个报错我们可以完美的和上述的一些推论吻合,MPEG4Extractor的parsechunk处报出了这个log,说明前面的Media读取外部输入的猜测是正确的。当然我们还得看一下其他的log。

其中MEtadataRetriverClient 出现了6次,audio_hw_primary出现了41次,msm8974-platform出现了17次,ACDB-LOADER出现了24次,AudioFlinger出现了111次。还有一些其他的log,但是距离crash位置太远,且频率不高,关注度下降。

结合Log和pc得出一些结论:

  1. Blinder调用AudioPolicyService-getOutput,这段代码位于AudioFlinger.cpp中,所以引发了大量的Flinger的log。
  2. ACDB为loader的log,并且没有报错,意义不大。
  3. msm8974是芯片的log,是用于音频校准的,意义不大。
  4. MetadataRetrieverClient是用于获取 元数据的,符合读入变量的准则。
  5. 由于MP4所以MetadataRetrieverClient调用的MediaExtractor::MPEG4Extractor,用于分离音视频轨道,是读取外部信息的最终实现函数。

综合MainLog和PC得出一个结论:

最大可能出现的问题就是MPEG4Extractor在读取MP4时,导致的内存溢出,从而覆盖了StrongPoint。

重点关注:libstagefright中的MPEG4Extractor函数,基本可以通过经验判断最大的变量在此输入,最有可能的crash root cause也应该在此之后出现!

有过经验的读者,必然会思考一个问题,如果这个错误的MP4它包含了width和height,那么最后一句话E MPEG4Extractor: No width or height, assuming worst case 1080p不报错,我们该如何定位到这个错误。或许根本不知道是MP4这个文件?

某些重要的信息可能远离crash点。所以其实在Tomb中,有留下这两句很重要的log信息,I SoftMPEG4Encoder: Construct SoftMPEG4Encoder。这其实就可以说如果没有MPEG4Extractor这个报错,我们依然能能够推测到是读取MP4文件出了错误,但是比较麻烦的是它出现的距离crash较远,而且不是E报错而是一个简单的I,如何关注它确实是一个需要经验的事情。而且哪怕知道是读取一个MP4,这依然是一个巨大的挑战如何来定位最终实现的函数?

如果Crash Location如此简单,可能就不需要运维团队了。所以其实很多情况的Tombstone会很麻烦,而且哪怕一个相同的root cause,也可能会有完全不同的tombstone。这里我们手动打开错误的MP4来进行一个人工出发的crash。这样产生的Tombstone会是什么结果呢?我们已经事先知道了这2个crash report应该是同一个原因造成的。

3、比较一下错误的产生头部

Build fingerprint: 'Android/aosp_bullhead/bullhead:6.0.1/MTC19V/hypo08180946:userdebug/test-keys'
Revision: 'rev_1.0'
ABI: 'arm'
pid: 1930, tid: 2011, name: Binder_1  >>> /system/bin/mediaserver <<<
signal 7 (SIGBUS), code 1 (BUS_ADRALN), fault addr 0x5a5a5a5d

和之前的不同是这个报错是SIGBUS(内存地址有效,但不能正常使用该指针)。BUS_ADRALN(错误的内存分配使得地址不对齐)。一般来说大概率这样的问题都和malloc操作的失误,或者内存操作的失误有关。

但相较于之前更明显的内存非法写的报错,这个报错更加的含义多而且难以解释。

我们看到错误的地址是0x5a5a5d,和之前的也不一样,(注:笔者已经禁用了ASLR),这样的情况告诉我们,哪怕是一个根本原因,不同的触发方式,不同的路径得到的crash report可能天差地别。其实这也从一个角度印证了以前的论文和开发者普遍提到的一个头疼现象,很多看似完全不同路径,不同错误的crash report,指向的却是一个相同的root cause,那么手动分析将会耗费开发者难以计数的时间和头发。
再看backtrace的pc记录

backtrace:
    #00 pc 0000e602  /system/lib/libutils.so (_ZN7android7RefBase12weakref_type7incWeakEPKv+5)
    #01 pc 0000e739  /system/lib/libutils.so (_ZN7android7RefBase12weakref_type16attemptIncStrongEPKv+8)
    #02 pc 0000c66f  /system/lib/libstagefright_foundation.so (_ZN7android13ALooperRoster23unregisterStaleHandlersEv+90)
    #03 pc 0000b941  /system/lib/libstagefright_foundation.so (_ZN7android7ALooperC2Ev+100)
    #04 pc 0004dee5  /system/lib/libmediaplayerservice.so (_ZN7android14NuPlayerDriverC1Ei+116)
    #05 pc 00042475  /system/lib/libmediaplayerservice.so
    #06 pc 00041e83  /system/lib/libmediaplayerservice.so (_ZN7android18MediaPlayerFactory12createPlayerENS_11player_typeEPvPFvS2_iiiPKNS_6ParcelEEi+82)
    #07 pc 00043c59  /system/lib/libmediaplayerservice.so (_ZN7android18MediaPlayerService6Client12createPlayerENS_11player_typeE+80)
    #08 pc 00043d47  /system/lib/libmediaplayerservice.so (_ZN7android18MediaPlayerService6Client17setDataSource_preENS_11player_typeE+10)
    #09 pc 0004408f  /system/lib/libmediaplayerservice.so (_ZN7android18MediaPlayerService6Client13setDataSourceEixx+214)
    #10 pc 0007a4a7  /system/lib/libmedia.so (_ZN7android13BnMediaPlayer10onTransactEjRKNS_6ParcelEPS1_j+502)
    #11 pc 000199c9  /system/lib/libbinder.so (_ZN7android7BBinder8transactEjRKNS_6ParcelEPS1_j+60)
    #12 pc 0001ed53  /system/lib/libbinder.so (_ZN7android14IPCThreadState14executeCommandEi+550)
    #13 pc 0001eebd  /system/lib/libbinder.so (_ZN7android14IPCThreadState20getAndExecuteCommandEv+64)
    #14 pc 0001ef21  /system/lib/libbinder.so (_ZN7android14IPCThreadState14joinThreadPoolEb+48)
    #15 pc 000239c1  /system/lib/libbinder.so
    #16 pc 00010071  /system/lib/libutils.so (_ZN7android6Thread11_threadLoopEPv+112)
    #17 pc 0003f883  /system/lib/libc.so (_ZL15__pthread_startPv+30)
    #18 pc 00019f75  /system/lib/libc.so (__start_thread+6)

按照我们前面所说的,这里也需要使用add2line工具,不过我们可以首先将前面的Thread创建,libbinder通性等过程忽略。

4、直接分析对应的.so文件

直接从libmedia.so开始进行分析。

也不多扯就上分析结果

#00 pc const int32t c = android_atomic_inc(&ref->mStrong)
#01 pc <sp>... mptr->incStrong
#02 pc sp<T> set-pointer
#03 pc ALooper:glooperRoster->unregisterHandlers
#04 pc mLooper(ALooper)
#05 pc sp<mediaPlayerBase> createPlayer()
#06 pc p= factory->createPlayer
#07 pc Media::client:create()
#08 pc sp<mediaPlayerBase> p=createPlayer
#09 pc sp<mediaPlayerBase> p=setDataSource
#10 pc libmedia.so  BnAudioPolicyService::OnTransact——————rely->writeInt32...

可以看到其实pc帧从5-9基本都是再说同一个过程,就是设置数据源,创造播放器。然后看pc帧从3-4基本上也是ALooper的操作,又涉及多线程。

看到这里是不是令人头裂,因为感觉这个和之前的剧本不太一样,甚至可以说完全懵逼。但是不要着急,我们相信计算机的世界没有什么玄学。我们定睛一看,#00pc和#01pc和#10pc其实和我们上面讲的一样

首先从PC中我们可以大致得出这个程序的路线:

Java层到Native层的Flinger——libmediaplayerservice进行Player的创建——ALooper多线程调度——StrongPoint出错,同样造成了非法的内存写。

在这里插入图片描述

对比一下第一次传输pc后自启动的播放器backtrace:

Java层到Native层的Flinger——>libmedia进行onTransact通信——>AudioPolicyService服务启动获取外部信息——>AudioPolicyManager启动使设备获得输入——>StrongPoint出错,造成了非法的内存写

请添加图片描述

中间路径光看pc帧的记录可以说是完全不同,这其中固然有第一次是自启动需要更改配置等问题,也是因为两种触发的方式不同,而且tombstone的记录方式虽然记录下来的绝对发生过,但是发生过的可能并未记录。但我们依然能从backtrace中得知一些信息,即错误发生在libmediaplayerservice进行播放器的创建后,在线程之间切换StrongPoint时出现错误,而且内存非法写的问题最大的可能就是原指针地址被覆盖或者指针无效。
进入Mainlog

笔者进行了一个统计:

audio_hw_primary 33次,ACDB-LOADER 22次,msm8974 12次,NuPlayerDriver
12次,MPEG4EXtractor 12次,GenericSource 2次,MetadataRetriverClient
10次,Audio Flinger 117次。

当然最关注的还是在crash点最接近的log

06-18 01:47:12.809  1930  2011 E MetadataRetrieverClient: failed to extract an album art
06-18 01:47:12.810  1930  1930 E MetadataRetrieverClient: failed to capture a video frame
06-18 01:47:13.285  1930  2001 D audio_hw_primary: disable_audio_route: reset and update mixer path: low-latency-playback
06-18 01:47:13.286  1930  2001 D audio_hw_primary: disable_snd_device: snd_device(2: speaker)
06-18 01:47:23.033  1930  2012 W AudioFlinger: session id 18 not found for pid 1445

看到了MetadataRetrieveClient,这是个读取媒体源数据的函数。看到了许多的报错,比如无法获取一个专辑封面,无法捕捉视频帧。这完全不是一个正常的media应该出现的报错!没有封面也就算了,无法捕捉视频帧是完全无法理解的,这其实在极富经验的debuger看来,已经可以知道大致的错误了。

GenericSource很有趣,他只出现了2次,并且都是Error级别log,而且报错的Log直指核心问题:Failed to init from data source。由于GenericSource的报错非常的核心,我搜索了一下它的代码位置,是NuMediaplayer::GenericSource读取本地文件。由于我们这次是手动触发的Media,所以我们不得不借助了一个NuMediaplayer来打开这个MP4,但通过前面的一些痕迹,所以哪怕笔者不说,读者想必也能知道NuMediaplayer肯定是调用了GenericSource来打开本地文件。

这里比较有趣的是NuPlayerDriver和MetadataRetriverClient 和MPEG4EXtractor 的出现次数非常的接近,而且这些Log出现的顺序似乎在疯狂的重复,导致出现了极度类似的Log块。

06-18 01:47:10.327 1930 1930 D NuPlayerDriver: reset(0xf4968640)
06-18 01:47:10.328 1930 1930 D NuPlayerDriver: notifyListener_l(0xf4968640), (8, 0, 0)
06-18 01:47:10.329 1930 2022 D NuPlayerDriver: notifyResetComplete(0xf4968640)
06-18 01:47:10.370 1930 2013 E MPEG4Extractor: No width or height, assuming worst case 1080p
06-18 01:47:10.380 1930 2013 E MetadataRetrieverClient: failed to extract an album art
06-18 01:47:10.381 1930 2011 E MetadataRetrieverClient: failed to capture a video frame
06-18 01:47:10.853 1930 2001 D audio_hw_primary: disable_audio_route: reset and update mixer path: low-latency-playback
06-18 01:47:10.856 1930 2001 D audio_hw_primary: disable_snd_device: snd_device(2: speaker)
06-18 01:47:12.055 1930 2028 D NuPlayer: onSetVideoSurface(0xf6730300, no video decoder)
06-18 01:47:12.079 1930 2029 E MPEG4Extractor: No width or height, assuming worst case 1080p
06-18 01:47:12.092 1930 2029 E GenericSource: Failed to init from data source!
06-18 01:47:12.095 1930 2028 D NuPlayerDriver: notifyListener_l(0xf4968640), (100, 1, -2147483648)
06-18 01:47:12.750 1930 1930 D NuPlayerDriver: reset(0xf4968640)
06-18 01:47:12.751 1930 1930 D NuPlayerDriver: notifyListener_l(0xf4968640), (8, 0, 0)
06-18 01:47:12.752 1930 2028 D NuPlayerDriver: notifyResetComplete(0xf4968640)
06-18 01:47:12.754 1930 2013 D NuPlayerDriver: reset(0xf4968640)

看了下NuPlayerDriver的代码,发现如果创建失败他会反复尝试,确实就会出现这种奇怪的现象。但是这其实对我们是好事儿,因为反复强调反复操作的地方,出错的概率其实更大。
那么结合起来看

Java层到Native层的Flinger——libmediaplayerservice进行Player的创建——NuMediaplayer的创建——GenericSource读取本地文件——MetadataRetrieverClient读取Media文件——MPEG4Extractor解析MP4文件——ALooper多线程调度——StrongPoint出错,同样造成了非法的内存写。

ok~嫌疑最大依然是MPEG4Extractor,虽然路径截然不同,但是最后我们发现最大嫌疑依然是MPEG4Extractor的cpp是否出现了错误。

当然读者的疑问我已经想到了:

1、这样子tombstone里面的寄存器和stack和memory map直接被我吃了么?
完全从头到尾似乎就没这2个东西,咳咳,当然不,主要是这2块部分的难度更高,所以笔者会在后面提到。

2、怎么样给出一个可以证明的细致调度信息?
毕竟我们前面全都是很粗的路径,甚至还需要一些经验知识。这不得不说明一个问题,tombstone毕竟只是一个crash report,如果想要精确到函数调用链,我们不得不结合源代码给出静态分析。

相信大家如果看过应该知道我们基本可以推测出错误的大致代码位置和错误的表现。但是笔者在前两章中其实故意避开了一些概念性的问题,那么可能会让大家产生一些疑惑,在这里做一个总体的解释。

首先不要看前面笔者通过各种操作找到了crash可能出现的地方,但阅读总是简单的,执行起来还是需要耗费大量时间和工作人员的经验。crash的location是一个业界尚未解决的难题,目前多数的工作试图通过1、逆序执行。2、大量crash report的偏统计分析。从而来定位一个错误的概率。

也看到过一些论文,尝试通过stack的信息来构建崩溃时的执行流,但是这其实效果不佳,原因也很简单——crash点根本在stack上没有记录,root cause也不在stack上。拿我们本次的libstagefright造成的内存越界为例子:

stack:
         f3878490  00000018
         f3878494  f711fed1  /system/lib/libc.so (je_malloc+472)
         f3878498  00000010
         f387849c  f748e5ac  /system/lib/libicuuc.so
         f38784a0  00000038
         f38784a4  00000000
         f38784a8  f72c635b  /system/lib/libmediaplayerservice.so
         f38784ac  f67c7080  /dev/__properties__
         f38784b0  f3878604  [stack:2011]
         f38784b4  00000000
         f38784b8  fffffd9c
         f38784bc  f72e5078  /system/lib/libmediaplayerservice.so
         f38784c0  00000060
         f38784c4  f711fed1  /system/lib/libc.so (je_malloc+472)
         f38784c8  00000000
         f38784cc  00000005
    #00  f38784d0  00000060
         ........  ........
    #01  f38784d0  00000060
         f38784d4  f70e6b39  /system/lib/libc.so (_ZL13find_propertyP7prop_btPKchS2_hb+124)
         f38784d8  00000000
         f38784dc  f72c635b  /system/lib/libmediaplayerservice.so
         f38784e0  f6c17020  /system/lib/libstagefright_foundation.so
         f38784e4  f6c17020  /system/lib/libstagefright_foundation.so
         f38784e8  00000000
         f38784ec  f6c09673  /system/lib/libstagefright_foundation.so (_ZN7android13ALooperRoster23unregisterStaleHandlersEv+94)

如果没有backtrace,光看stack信息,试问谁能发现最后的崩溃是libutils中的incweak内存非法写造成的崩溃,并且连root cause的libstagefright.so甚至都没有出现在stack中。所以对单纯的stack信息,在内存越界这种不可预测崩溃结果的错误中,如果崩溃并未出现在stack中,stack-only的手段是无用而浪费时间的。

所以笔者这里点出现在一个业界中巨大的难题:crash点和root cause隐藏于整个control flow中,并不一定会在stack中留下痕迹。

还有的问题其实在本次例子中不是很明显,即:程序多线程造成的调度错误,root cause并不一定出现在crash的线程中,和log main的顺序混乱。

这其实是前两章中不一开始就使用stack信息和memory dump等的一个原因。不先使用这些不代表笔者忽略了这些,只是这些的价值可能相较于backtrace和main log较低。但是我们这里不是说就完全放弃了这些东西,在本章我们将展示一下stack,寄存器,memory map对于tombstone信息的丰富作用。

STACK:

首先stack信息由于它往往记录了函数的调用关系,所以被许多的工作视为重要的信息来源。但其实在tombstone里面有backtrace的记录,这样的函数调用关系可能重要度会下降。不过stack中保存了一些函数调用的参数和一些更细节的函数调用关系,可以作为一个很好地信息补充。在后续control flow的构建中将会丰富细节,使得control flow更加的清晰。
寄存器:

首先关注重要的寄存器:r0,ip,sp,lr,pc等

寄存器含义
r0重要的参数寄存器
lr本段程序执行完的返回地址
pc当前执行指令地址
sp当前栈指针地址
ip和cs:ip组成指令地址
fp帧指针
signal 7 (SIGBUS), code 1 (BUS_ADRALN), fault addr 0x5a5a5a5d
    r0 5a5a5a5d  r1 f38784f4  r2 fffffff0  r3 00000000
    r4 5a5a5a59  r5 f38784f4  r6 00000004  r7 f38784f4
    r8 f38784f8  r9 00000000  sl f4968e90  fp f6c17024
    ip f724dc24  sp f38784d0  lr f724273d  pc f7242602  cpsr 000f0030

看本例中,fault addr是0x5a5a5d,说明此时错误的内存地址是0x5a5a5d。看到r0也是5a5a5d,大致就明白了r0记录的是这个地址的参数寄存器。转到0x5a5a5d的寄存器附近

memory near r0:
    5a5a5a3c -------- -------- -------- --------  ................
    5a5a5a4c -------- -------- -------- --------  ................
    5a5a5a5c -------- -------- -------- --------  ................
    5a5a5a6c -------- -------- -------- --------  ................
    5a5a5a7c -------- -------- -------- --------  ................
    5a5a5a8c -------- -------- -------- --------  ................
    5a5a5a9c -------- -------- -------- --------  ................
    5a5a5aac -------- -------- -------- --------  ................
    5a5a5abc -------- -------- -------- --------  ................
    5a5a5acc -------- -------- -------- --------  ................
    5a5a5adc -------- -------- -------- --------  ................
    5a5a5aec -------- -------- -------- --------  ................
    5a5a5afc -------- -------- -------- --------  ................
    5a5a5b0c -------- -------- -------- --------  ................
    5a5a5b1c -------- -------- -------- --------  ................
    5a5a5b2c -------- -------- -------- --------  ................

大致就明白了确实是内存地址被恶意覆盖了,指向了一个不能进行写操作的地方。此时的ip和lr是f7242602和f724273d。我们对比memory map,可以知道都存在于libutils中,那么此时就很符合pc的第一条libutils的指令。在逆序执行中很可能会需要利用到,但是本次我们并未有这么做,因为是人工的debug,所以你肉眼再强怕是也很难直接看出寄存器的是使用到哪个函数的。不逆序执行的话,寄存器的值着实很难带来更多的信息量。
Memory map:

内存map的存在其实指出了这个程序运行中使用了哪些程序。这可以类似一个拼图一样,给出一些最基本的程序节点。就比如在所有的memory map中,libstagefright.so都出现了,但是这个so却从未出现在stack和backtrace中,从mainlog中获得它的信息后,找到了它对应的内存map。

可以印证确实出现过libstagefright这个.so。

总结

本章中,介绍了一下关于crash location的目前的业界难题,和对于以前一些未用到的信息的解释。大家可以看到tombstone固然是提供了一些信息量,让我们能够大致理解崩溃的错误原因,路径之间的语义,根本错误的发生地址,但是它也有它的局限性。我们光从tombstone已经很难再进行更精确的分析或者验证了,所以我们不得不引入其他的工作。

这些工作可能有——代码静态分析,语义的自动解释,变量输入点的定位等等。

其他工作敬请期待,我们的目标是星辰大海!

本文转载学习自:https://zhuanlan.zhihu.com/p/195479185

  • 19
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Lotay_天天

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

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

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

打赏作者

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

抵扣说明:

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

余额充值