自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

黑客與宕機(jī)

安全 應(yīng)用安全
本文作者將和大家分享內(nèi)存轉(zhuǎn)儲(chǔ)分析的底層邏輯和方法論,并通過一個(gè)線上真實(shí)案例來展示從分析到得出結(jié)論的整個(gè)過程,希望對(duì)同學(xué)們處理此類問題和對(duì)系統(tǒng)的理解上有所幫助。

[[334060]]

造成系統(tǒng)異常宕機(jī)(無響應(yīng)、異常重啟)的原因有很多種,最常見的是操作系統(tǒng)內(nèi)部缺陷和設(shè)備驅(qū)動(dòng)缺陷。本文作者將和大家分享內(nèi)存轉(zhuǎn)儲(chǔ)分析的底層邏輯和方法論,并通過一個(gè)線上真實(shí)案例來展示從分析到得出結(jié)論的整個(gè)過程,希望對(duì)同學(xué)們處理此類問題和對(duì)系統(tǒng)的理解上有所幫助。

相信凡是與計(jì)算機(jī)高頻親密接觸的人,都遇到過系統(tǒng)無響應(yīng),或突然重啟的情況。這樣的情況如果發(fā)生在客戶端設(shè)備,如手機(jī),或者筆記本電腦上,且不是頻繁出現(xiàn),基本上我們的解法就是鴕鳥算法,即默默重啟設(shè)備,然后繼續(xù)使用,當(dāng)作什么都沒發(fā)生過。

但是,如果這樣的問題發(fā)生在服務(wù)端,比如運(yùn)行微信、微博后臺(tái)程序的虛擬機(jī)或者物理機(jī)上,那往往會(huì)產(chǎn)生相當(dāng)嚴(yán)重的影響。輕則導(dǎo)致業(yè)務(wù)中斷,重則導(dǎo)致業(yè)務(wù)長(zhǎng)時(shí)間無法工作。

大家都知道,驅(qū)動(dòng)這些計(jì)算機(jī)的是運(yùn)行在其上的操作系統(tǒng),如 Windows 或者 Linux 等。系統(tǒng)異常宕機(jī)(無響應(yīng)、異常重啟)的原因有很多種,但總體來看,操作系統(tǒng)內(nèi)部缺陷,或者設(shè)備驅(qū)動(dòng)缺陷是最常見的兩類原因。

從根本上解決這類問題“唯一正確”的方法,是操作系統(tǒng)內(nèi)存轉(zhuǎn)儲(chǔ)分析(Memory Dump Analysis)。內(nèi)存轉(zhuǎn)儲(chǔ)分析屬于高階的軟件調(diào)試能力,需要工程師有豐富且全面的系統(tǒng)級(jí)別理論知識(shí)和大量的疑案破解似的上手實(shí)踐經(jīng)驗(yàn)。

內(nèi)存轉(zhuǎn)儲(chǔ)分析的方法論

內(nèi)存轉(zhuǎn)儲(chǔ)分析是對(duì)專業(yè)能力要求極高的一個(gè)工作,也是非常不容易的一件事情。在以往案例分享后,得到比較有趣的反饋,如“耳邊想起了柯南的配音”,或者“真黑貓警長(zhǎng)!做的是 IT 工程師,卻整天搞刑偵工作”。

內(nèi)存轉(zhuǎn)儲(chǔ)分析需要用到的基礎(chǔ)能力,包括但不限于反匯編、匯編分析、各種語言的代碼分析,系統(tǒng)層面各種結(jié)構(gòu)的理解,如堆,棧,虛表等,甚至深入到 bit 級(jí)別。

試想,一個(gè)系統(tǒng)運(yùn)行了很長(zhǎng)一段時(shí)間。在這段時(shí)間里,系統(tǒng)積累了大量正常、甚至不正常的狀態(tài)。這時(shí)如果系統(tǒng)突然出現(xiàn)了一個(gè)問題,那這個(gè)問題十有八九跟長(zhǎng)時(shí)間積累下來的狀態(tài)有關(guān)系。

分析內(nèi)存轉(zhuǎn)儲(chǔ),就是分析發(fā)生問題時(shí),系統(tǒng)產(chǎn)生的“快照”。實(shí)際上需要工程師以這個(gè)快照為出發(fā)點(diǎn),追溯歷史,找出問題發(fā)生源頭。這有點(diǎn)像是從案發(fā)現(xiàn)場(chǎng),推理案發(fā)經(jīng)過一樣。

死鎖分析方法

內(nèi)存轉(zhuǎn)儲(chǔ)分析方法,可以從所要解決問題的角度,簡(jiǎn)單分成兩類,分別是死鎖分析方法,和異常分析方法。這兩種方法的區(qū)別在于,死鎖分析方法以系統(tǒng)全局為出發(fā)點(diǎn),而異常分析則從具體異常點(diǎn)開始。

死鎖問題表現(xiàn)出來,就是系統(tǒng)不響應(yīng)問題。死鎖分析方法著眼于全局。這里的全局,就是整個(gè)操作系統(tǒng),包括所有進(jìn)程在內(nèi)的系統(tǒng)全貌。我們從教科書里學(xué)到的知識(shí),一個(gè)運(yùn)行中的程序,包括了代碼段,數(shù)據(jù)段和堆棧段。用這個(gè)方法去看一個(gè)系統(tǒng)也同樣適合。系統(tǒng)的全貌,其實(shí)就包括正在被執(zhí)行的代碼(線程),和保存狀態(tài)的數(shù)據(jù)(數(shù)據(jù)、堆棧)。

死鎖的本質(zhì),是系統(tǒng)中部分或者全部線程,進(jìn)入了互相等待且互相依賴的狀態(tài),使得進(jìn)程所承載的任務(wù)無法被繼續(xù)執(zhí)行了。所以我們分析這類問題的中心思想,就是分析系統(tǒng)中所有的線程的狀態(tài)和它們之間的依賴關(guān)系,正如如圖 1 所示。

 

圖 1

線程的狀態(tài)相對(duì)來說是比較確定的信息。我們可以通過讀取內(nèi)存轉(zhuǎn)儲(chǔ)中線程的狀態(tài)標(biāo)志位,來獲取這類的信息。而依賴關(guān)系分析則需要很多的技巧和實(shí)踐經(jīng)驗(yàn)。最常用分析方法有對(duì)象的持有等待關(guān)系分析,時(shí)序分析等。

異常分析方法

相對(duì)死鎖分析,異常分析方法的核心是異常。我們經(jīng)常遇到的異常有除零操作,非法指令執(zhí)行,錯(cuò)誤地址訪問,甚至包括軟件層面自定義的非法操作等。這些異常反應(yīng)到操作系統(tǒng)層面,就是異常重啟類宕機(jī)問題。

異常問題歸根結(jié)底是處理器執(zhí)行了具體的指令而觸發(fā)的。換句話說,我們看到的現(xiàn)象,肯定是處理器踩到了異常點(diǎn)。所以分析異常類問題,我們需要從異常點(diǎn)出發(fā),逐步地推導(dǎo)出代碼執(zhí)行到這一點(diǎn)的完整邏輯。

以經(jīng)驗(yàn)來看,懂得做內(nèi)存轉(zhuǎn)儲(chǔ)異常分析的工程師不多,而理解以上一點(diǎn)的人更是少之又少。很多工程師分析異常重啟問題,基本上只停留在異常本身,根本沒有推導(dǎo)出問題背后的整個(gè)邏輯。

相比死鎖分析方法,異常分析的方法沒有那么多固定的章法,甚至很多時(shí)候,因?yàn)閱栴}邏輯復(fù)雜,我們沒有辦法找出根本原因。

總體看來,異常分析的底層邏輯,是不斷地對(duì)比預(yù)期和非預(yù)期的狀況,然后找出背后的原因。比如處理其執(zhí)行了錯(cuò)誤指令而觸發(fā)的異常,那我們需要從回答正常被執(zhí)行的指令應(yīng)該是什么,為什么處理器拿到了這個(gè)錯(cuò)誤指令這兩個(gè)問題開始,不斷深入,追根究底。

用死鎖分析方法處理異常問題,用異常分析方法處理死鎖問題

以上兩種內(nèi)存轉(zhuǎn)儲(chǔ)分析方法,是基于問題分析的起點(diǎn)和一般性分析手段來分類的。在實(shí)際問題處理過程中,我們經(jīng)常需要從系統(tǒng)全局狀態(tài)中,找到進(jìn)一步處理異常問題的思路,也會(huì)用具體細(xì)節(jié)分析手段,來給全局類問題最后一擊。

黑客與宕機(jī)

問題背景

宕機(jī)問題有一種比較少見的問題模式,就是看起來完全不相關(guān)的機(jī)器同時(shí)出現(xiàn)宕機(jī)。處理這個(gè)模式的問題,我們需要找到在這些機(jī)器上能同時(shí)觸發(fā)問題的條件。

通常,這些機(jī)器要么幾乎在同一時(shí)間點(diǎn)出現(xiàn)問題,要么從某一個(gè)時(shí)間點(diǎn)開始,相繼出現(xiàn)問題。對(duì)于前一種情況,比較常見的情形是,物理設(shè)備故障導(dǎo)致運(yùn)行在其上的所有虛擬機(jī)宕機(jī),或者一個(gè)遠(yuǎn)程管理軟件同時(shí)殺死了多個(gè)系統(tǒng)的關(guān)鍵進(jìn)程;對(duì)于后一種情況,可能的一個(gè)原因是,用戶在所有實(shí)例上部署了同一個(gè)有問題的模塊(軟件、驅(qū)動(dòng))。

而實(shí)例被大范圍地攻擊,則是另一個(gè)常見的原因。比如在 WannaCry 勒索病毒肆虐的時(shí)候,經(jīng)常出現(xiàn)一些公司,或者一些部門的機(jī)器全部藍(lán)屏的情形。

在這個(gè)案例中,用戶安裝了阿里云的云監(jiān)控產(chǎn)品之后,出現(xiàn)了大范圍云服務(wù)器連續(xù)宕機(jī)的情況。為了自證清白,我們耗費(fèi)了不少體力腦力來深入分析這個(gè)問題。通過此案例分享,希望能給讀者以啟發(fā)。

壞掉的內(nèi)核棧

我們處理操作系統(tǒng)宕機(jī)類問題的唯一正確方法是內(nèi)存轉(zhuǎn)儲(chǔ)。不管是 Linux 或 Windows,在系統(tǒng)宕機(jī)之后,都能夠通過自動(dòng),或者人工的方式,產(chǎn)生內(nèi)存轉(zhuǎn)儲(chǔ)。

分析 Linux 內(nèi)存轉(zhuǎn)儲(chǔ)的第一步,我們使用 crash 工具打開內(nèi)存轉(zhuǎn)儲(chǔ),并用 sys 命令觀察系統(tǒng)的基本信息和宕機(jī)的直接原因。對(duì)于這個(gè)問題來說,宕機(jī)的直接原因是"Kernel panic - not syncing: stack-protector: Kernel stack is corrupted in: ffffxxxxxxxx87eb",如圖 2 所示。

 

圖2

關(guān)于這條信息,我們必須逐字解讀。"Kernel panic - not syncing:" 這部分內(nèi)容在內(nèi)核函數(shù)panic 里輸出,凡是調(diào)用到 panic 函數(shù),必然會(huì)有這一部分輸出,所以這一部分內(nèi)容和問題沒有直接關(guān)系。而"stack-protector: Kernel stack is corrupted in:" 這部分內(nèi)容,在內(nèi)核函數(shù)__stack_chk_fail,這個(gè)函數(shù)是一個(gè)堆棧檢查函數(shù),它會(huì)檢查堆棧,同時(shí)在發(fā)現(xiàn)問題的時(shí)候調(diào)用panic 函數(shù)產(chǎn)生內(nèi)存轉(zhuǎn)儲(chǔ)報(bào)告問題。

而它報(bào)告的問題是堆棧損壞。關(guān)于這個(gè)函數(shù),后續(xù)我們會(huì)進(jìn)一步分析。

而 ffffxxxxxxxx87eb 這個(gè)地址,是函數(shù) __builtin_return_address(0) 的返回值。當(dāng)這個(gè)函數(shù)的參數(shù)是 0 的時(shí)候,這個(gè)函數(shù)的輸出值是調(diào)用它的函數(shù)的返回地址。這句話現(xiàn)在有點(diǎn)繞,但是后續(xù)分析完調(diào)用棧,問題就會(huì)變得很清楚。

函數(shù)調(diào)用棧

分析宕機(jī)問題的核心,就是分析 panic 的調(diào)用棧。圖 3 中的調(diào)用棧,乍看起來是system_call_fastpath 調(diào)用了 __stack_chk_fail,然后 __stack_chk_fail 調(diào)用了 panic,報(bào)告了堆棧損壞的問題。但是稍微和類似的堆棧作一點(diǎn)比較的話,就會(huì)發(fā)現(xiàn),事實(shí)并非這么簡(jiǎn)單。

 

圖3

圖 4 是一個(gè)類似的,以 system_call_fastpath 函數(shù)開始的調(diào)用棧。不知道大家有沒有看出來這個(gè)調(diào)用棧和上邊調(diào)用棧的不同。實(shí)際上,以 system_call_fastpath 函數(shù)開始的調(diào)用棧,表示這是一次系統(tǒng)調(diào)用(system call)的內(nèi)核調(diào)用棧。

 

圖4

圖 4 的調(diào)用棧,表示用戶模式的進(jìn)程,有一次 epoll 的系統(tǒng)調(diào)用,然后這個(gè)調(diào)用進(jìn)入了內(nèi)核模式。而圖 3 中的調(diào)用棧顯然是有問題的,因?yàn)槲覀兙退悴楸樗械奈臋n,也不會(huì)找到一個(gè)系統(tǒng)調(diào)用,會(huì)對(duì)應(yīng)于內(nèi)核 __stack_chk_fail 函數(shù)。

這里需要提醒的是,這邊引出另外一個(gè),在分析內(nèi)存轉(zhuǎn)儲(chǔ)的時(shí)候需要注意的問題,就是用 bt 打印出來的調(diào)用棧有的時(shí)候是錯(cuò)誤的。

所謂的調(diào)用棧,其實(shí)不是一種數(shù)據(jù)結(jié)構(gòu)。用 bt 打印出來的調(diào)用棧,其實(shí)是從真正的數(shù)據(jù)結(jié)構(gòu),線程內(nèi)核堆棧中,根據(jù)一定的算法重構(gòu)出來的。而這個(gè)重構(gòu)過程,其實(shí)是函數(shù)調(diào)用過程的一個(gè)逆向工程。

相信大家都知道堆棧的特性,即先進(jìn)后出。關(guān)于函數(shù)調(diào)用,以及堆棧的使用,可以參考圖 5??梢钥吹?,每個(gè)函數(shù)調(diào)用,都會(huì)在堆棧上分配到一定的空間。而 CPU 執(zhí)行每個(gè)函數(shù)調(diào)用指令 call,都會(huì)順便把這條 call 指令的下一條指令壓棧。這些“下一條指令”,就是所謂的函數(shù)返回地址。

 

圖5

這個(gè)時(shí)候,我們?cè)倩仡^看 Panic 的直接原因那一部分,即函數(shù) __builtin_return_address(0) 的返回值。

這個(gè)返回值,其實(shí)就是調(diào)用 __stack_chk_fail 的 call 指令的下一條指令,這條指令屬于調(diào)用者函數(shù)。這條指令地址被記錄為 ffffxxxxxxxx87eb。

如圖 6 所示,我們用 sym 命令查看這個(gè)地址臨近的函數(shù)名,顯然這個(gè)地址不屬于函數(shù)system_call_fastpath,也不屬于內(nèi)核任何函數(shù)。這也再次驗(yàn)證了,panic 調(diào)用棧是錯(cuò)誤的這個(gè)結(jié)論。

 

圖6

關(guān)于 raw stack,如圖7所示,我們可以用 bt -r 命令來查看。因?yàn)?raw stack 往往有幾個(gè)頁面,這里只截圖和 __stack_chk_fail 相關(guān)的這一部分內(nèi)容。

 

圖7

這部分內(nèi)容,有三個(gè)重點(diǎn)數(shù)據(jù)需要注意,panic 調(diào)用 __crash_kexec 函數(shù)的返回值,這個(gè)值是panic 函數(shù)的一條指令的地址;__stack_chk_fail 調(diào)用 panic 函數(shù)的返回值,同樣的,它是__stack_chk_fail 函數(shù)的一條指令的地址;ffffxxxxxxxx87eb 這個(gè)指令地址,屬于另外一個(gè)未知函數(shù),這個(gè)函數(shù)調(diào)用了 __stack_chk_fail。

Syscall number 和 Syscall table

因?yàn)閹в?system_call_fastpath 函數(shù)的調(diào)用棧,對(duì)應(yīng)著一次系統(tǒng)調(diào)用,而 panic 的調(diào)用棧是壞的,所以這個(gè)時(shí)候我們自然而然會(huì)疑問,到底這個(gè)調(diào)用棧對(duì)應(yīng)的是什么系統(tǒng)調(diào)用。

在 linux 操作系統(tǒng)實(shí)現(xiàn)中,系統(tǒng)調(diào)用被實(shí)現(xiàn)為異常。而操作系統(tǒng)通過這次異常,把系統(tǒng)調(diào)用相關(guān)的參數(shù),通過寄存器傳遞到內(nèi)核。在我們使用 bt 命令打印出調(diào)用棧的時(shí)候,我們同時(shí)會(huì)輸出,發(fā)生在這個(gè)調(diào)用棧上的異常上下文,也就是保存下來的,異常發(fā)生的時(shí)候,寄存器的值。

對(duì)于系統(tǒng)調(diào)用(異常),關(guān)鍵的寄存器是 RAX,如圖 8 所示。它保存的是系統(tǒng)調(diào)用號(hào)。我們先找一個(gè)正常的調(diào)用棧驗(yàn)證一下這個(gè)結(jié)論。0xe8 是十進(jìn)制的 232。

 

圖8

使用 crash 工具,sys -c 命令可以查看內(nèi)核系統(tǒng)調(diào)用表。我們可以看到,232 對(duì)應(yīng)的系統(tǒng)調(diào)用號(hào),就是 epoll,如圖 9 所示。

 

圖9

這個(gè)時(shí)候我們?cè)倩仡^看“函數(shù)調(diào)用棧”這節(jié)的圖 3,我們會(huì)發(fā)現(xiàn)異常上下文中 RAX 是 0。正常情況下這個(gè)系統(tǒng)調(diào)用號(hào)對(duì)應(yīng) read 函數(shù),如圖 10 所示。

 

圖10

從圖 11 中,我們可以看出,有問題的系統(tǒng)調(diào)用表顯然是被修改過的。修改系統(tǒng)調(diào)用表(system call table)這種事情,常見的有兩種代碼會(huì)做,這個(gè)相當(dāng)辯證。一種是殺毒軟件,而另外一種是病毒或木馬程序。當(dāng)然還有另外一種情況,就是某個(gè)蹩腳的內(nèi)核驅(qū)動(dòng),無意識(shí)地改寫了系統(tǒng)調(diào)用表。

另外我們可以看到,被改寫過的函數(shù)的地址,顯然和最初被 __stack_chk_fail 函數(shù)報(bào)出來的地址,是非常鄰近的。這也可以證明,系統(tǒng)調(diào)用確實(shí)是走進(jìn)了錯(cuò)誤的 read 函數(shù),最終踩到了__stack_chk_fail 函數(shù)。

 

圖11

Raw data

基于上邊的數(shù)據(jù),來完全說服客戶,總歸還是有點(diǎn)經(jīng)驗(yàn)主義。更何況,我們甚至不能區(qū)分,問題是由殺毒軟件導(dǎo)致的,還是木馬導(dǎo)致的。這個(gè)時(shí)候我們花費(fèi)了比較多的時(shí)間,嘗試從內(nèi)存轉(zhuǎn)儲(chǔ)里挖掘出 ffffxxxxxxxx87eb 這個(gè)地址更多的信息。

有一些最基本的嘗試,比如嘗試找出這個(gè)地址對(duì)應(yīng)的內(nèi)核模塊等等,但是都無功而返。這個(gè)地址既不屬于任何內(nèi)核模塊,也不被已知的內(nèi)核函數(shù)所引用。這個(gè)時(shí)候,我們做了一件事情,就是把這個(gè)地址前后連續(xù)的,所有已經(jīng)落實(shí)(到物理頁面)的頁面,用 rd 命令打印出來,然后看看有沒有什么奇怪的字符串可以用來作為 signature 定位問題。

就這樣,我們?cè)卩徑刂钒l(fā)現(xiàn)了下邊這些字符串,如圖 12 所示。很明顯這些字符串應(yīng)該是函數(shù)名。我們可以看到 hack_open 和 hack_read 這兩個(gè)函數(shù),對(duì)應(yīng)被 hacked 的 0 和 2 號(hào)系統(tǒng)調(diào)用。還有函數(shù)像 disable_write_protection 等等。這些函數(shù)名,顯然說明這是一段“不平凡”的代碼。

 

圖12

后記

宕機(jī)問題的內(nèi)存轉(zhuǎn)儲(chǔ)分析,需要我們足夠的耐心。我個(gè)人的一條經(jīng)驗(yàn)是:every bit matters,就是不要放過任何一個(gè) bit 的信息。內(nèi)存轉(zhuǎn)儲(chǔ)因?yàn)闄C(jī)制本身的原因,和生成過程中一些隨機(jī)的因素,必然會(huì)有數(shù)據(jù)不一致的情況,所以很多時(shí)候,一個(gè)小的結(jié)論,需要從不同的角度去驗(yàn)證。

 

責(zé)任編輯:武曉燕 來源: 51CTO專欄
相關(guān)推薦

2012-09-13 09:52:20

2012-07-10 09:51:49

2023-07-31 14:12:08

2011-03-28 14:48:32

2010-09-17 08:53:01

2009-02-25 09:18:41

2021-07-06 12:32:49

黑客勒索軟件網(wǎng)絡(luò)攻擊

2009-12-15 10:57:05

2013-01-06 10:14:17

2009-08-21 14:51:33

2010-09-08 12:29:52

2011-05-30 13:29:06

2013-05-13 09:41:50

公有云服務(wù)宕機(jī)阿里云

2025-03-02 11:19:52

2012-08-27 09:37:19

2012-12-13 17:54:06

2011-05-04 16:58:26

2011-03-17 10:57:44

2012-07-30 11:15:41

2018-11-26 11:18:56

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)