由于一些工作原因,最近對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
從中我們可以看到如下信息:
- Android版本是6.01_MTC19V
- 手機版本是bullhead
- 系統是userdebug模式
- 底層框架是arm
- 出錯的文件夾位于mediaserver
- 錯誤內容是SIGSEGV(無效內存引用)和SEGV_MAPPERR(嘗試去寫只讀內存)
- 錯誤時的地址是0x41d00014
距離發現問題只有一步之遙。但直到這一步,你固然可以確定Android版本,手機版本,底層框架,出錯的大致文件夾,錯誤類型和錯誤地址。但你擁有了AOSP源碼后,你不會認為這些信息就足夠找到錯誤,甚至在茫茫代碼海中,連定位到一個比較準確的出錯cpp都做不到。但是萬事都得慢慢來,所以在這一步后,我們建立起初步的用來追尋root cause的基礎——AOSP的源代碼和Add2Line_arm的調試工具。(這一步如果不會,還是先補下基礎)
首先通過很多的文獻閱讀我們確定了一個觀念:
- 距離crash最近的.so文件觸發了這個crash。
- 真正的root cause可能并不是觸發這個crash的元兇,而隱藏在crash路徑鏈中。
- 相同的crash路徑應該相同。
但是由于這里的多線程原因,經過各種觀察,我們稍作更改這個結論:
- 距離crash最近的.so文件觸發了這個crash,觸發crash是由于一些特定的操作。
- 真正的root cause并不一定在crash線程中,也可能存在于其他的線程中。
- 多線程導致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得出一些結論:
- Blinder調用AudioPolicyService-getOutput,這段代碼位于AudioFlinger.cpp中,所以引發了大量的Flinger的log。
- ACDB為loader的log,并且沒有報錯,意義不大。
- msm8974是芯片的log,是用于音頻校準的,意義不大。
- MetadataRetrieverClient是用于獲取 元數據的,符合讀入變量的準則。
- 由于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 00000018f3878494 f711fed1 /system/lib/libc.so (je_malloc+472)f3878498 00000010f387849c f748e5ac /system/lib/libicuuc.sof38784a0 00000038f38784a4 00000000f38784a8 f72c635b /system/lib/libmediaplayerservice.sof38784ac f67c7080 /dev/__properties__f38784b0 f3878604 [stack:2011]f38784b4 00000000f38784b8 fffffd9cf38784bc f72e5078 /system/lib/libmediaplayerservice.sof38784c0 00000060f38784c4 f711fed1 /system/lib/libc.so (je_malloc+472)f38784c8 00000000f38784cc 00000005#00 f38784d0 00000060........ ........#01 f38784d0 00000060f38784d4 f70e6b39 /system/lib/libc.so (_ZL13find_propertyP7prop_btPKchS2_hb+124)f38784d8 00000000f38784dc f72c635b /system/lib/libmediaplayerservice.sof38784e0 f6c17020 /system/lib/libstagefright_foundation.sof38784e4 f6c17020 /system/lib/libstagefright_foundation.sof38784e8 00000000f38784ec 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 0x5a5a5a5dr0 5a5a5a5d r1 f38784f4 r2 fffffff0 r3 00000000r4 5a5a5a59 r5 f38784f4 r6 00000004 r7 f38784f4r8 f38784f8 r9 00000000 sl f4968e90 fp f6c17024ip 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