在硬盤留下后門,重裝系統都沒轍

jopen 11年前發布 | 24K 次閱讀 硬盤

        OHM2013(Observe, Hack, Make. )是一個專為黑客、制造者和那些有探究精神之人舉辦的國際戶外露營節,為期 5 天。今年 7 月 31 日在荷蘭 Geestmerambacht 舉辦,有 3000 人參與。

        這篇文章相關內容已在 OHM2013 已經公開。原理是利用硬盤的一些智能機制,在某個位置嵌入一些信息(比如:登錄信息),然后操作系統驗證用戶登陸時,會不自主地讀取黑客預留下的用戶名和密碼。

        簡介

        硬盤:如果你在看這篇文章,我肯定你起碼用過一兩個硬盤。硬盤很簡單,基本就是一些 512 字節的扇區,由遞增的數字標明地址,稱之為 LBA,也就是“邏輯塊尋址”。電腦可以向連接的硬盤的扇區中讀寫數據。通常會有個文件系統把這些扇區抽象成文件或文件夾。

在硬盤留下后門,重裝系統都沒轍

        如果你從這個幼稚的角度看硬盤,你會認為硬件應該也很簡單:你需要的就是個能連接 SATA 接口的東西,然后可以定位讀寫頭,從盤片上讀寫數據。但是可能不止這么簡單:硬盤不是還有處理壞塊、S.M.A.R.T.屬性的功能么?不是還有什么緩存需要管理的么?

        以上這些意味著硬盤中有些智能的東西,有智能就意味著可以黑掉它。我就喜歡可以黑的東西,于是我決定要看看硬盤是如何在非機械層面上工作的。這 種研究以前在很多硬件上做過:從筆記本的 PCI 擴展模塊到嵌入式控制器,甚至是蘋果的鍵盤。通常這些研究都是為了證明這些硬盤可以被破解,導致其受到軟件的影響,于是我決定達到同樣的目標:我要在這次 破解中讓硬盤繞過軟件安全機構。

        PCB 上的部件

        要想知道硬盤是否可以被破解,我需要更了解它們。如你們大多數一樣,我也有一摞或壞或舊的硬盤來一看究竟:

在硬盤留下后門,重裝系統都沒轍

        當然了,我們都知道硬盤的機械結構應該是好用的,我對那些部分也不感興趣。我的興趣在于大多數硬盤背面都有的那一小塊 PCB 板子,上面有 SATA 接口和電源接口。這種 PCB 看起來是這樣的:

在硬盤留下后門,重裝系統都沒轍

        可以看見 PCB 上有四塊芯片。接下來說說這些芯片:

在硬盤留下后門,重裝系統都沒轍

        這是一塊 DRAM(動態隨機存儲器)。這塊很好處理,芯片手冊很好找。這些芯片的容量一般在 8MB 到 64MB 之間,對應的就是硬盤標稱的緩存容量。

在硬盤留下后門,重裝系統都沒轍

        這個是電機控制器。這不是個標準器件,數據手冊不好找,但是這些控制器一般都有容易找的差不多的同系列產品。ST Smooth 控制器大概是最常用的一種了;除了驅動電機,它還能進行電源整流,還帶一些A/D變換通道。

在硬盤留下后門,重裝系統都沒轍

        這是一塊串行閃存。這個也好處理,容量一般在 64KB 到 256KB 之間。看起來這個是用來存儲硬盤控制器的啟動程序。有些硬盤沒有這個芯片,而是在控制器芯片內部有閃存來存儲程序。

在硬盤留下后門,重裝系統都沒轍

        這些小東西不是芯片,而是壓電震動傳感器。當硬盤受到撞擊時,它們可以把磁頭移到安全的地方,但是更有可能它在某個地方標記一個值,表示你的保修無效,因為是你自己摔的硬盤。

在硬盤留下后門,重裝系統都沒轍

        這里才是奇跡將要發生的地方:硬盤控制器。多是由 Marvell、ST 或者其他的 LSI 公司制造。有些硬盤廠商自己做控制器:我見過三星和西數就有自己的控制芯片。因為其他的部分都很好處理,這一塊才是我的興趣所在。

        不幸的是,這些芯片都沒有文檔。話說這些制造控制器的廠商不公開文檔有些不厚道真是說輕了:他們甚至在自己的網站上都不提這些芯片!更不幸的是,整個互聯網也幫不了我:搜這些芯片手冊只能找到沒有手冊的手冊網站,和賣芯片的中國廠商……

        那么,沒有最重要的芯片手冊,就意味著我們的計劃擱淺了么?

        連接 JTAG

        幸運的是,總有些辦法找到除了芯片手冊以外的有用信息。我就搜到這么一個。

        我找的是 HDDGuru 論壇上一個叫 Dejan 的人做的連接線。Dejan 不知怎么把他硬盤控制器的內部閃存廢掉了,然后想知道有沒有辦法,要么從外部閃存啟動控制器,要么重寫一下內部閃存。過了五天,沒人回應他,但是這哥們很 有創造力:他又發了個帖子說他找到了 JTAG 口的管腳。這真是個重大發現:JTAG 接口可以用來控制控制器。你可以用它啟動控制器、重啟、修改內存、設置斷點等等。然后 Dejan 發現了如何關掉控制器的啟動 ROM,找到了硬盤一個串口,然后試圖恢復他的閃存 ROM。后來他又提了一些關于更新閃存的過程,最后消失在茫茫人海中了。

        這些都是有用的信息:至少我知道了西部數據的控制器是 ARM 內核的,有 JTAG 接口。這些硬盤通常有串口,雖然沒有使用但是可以用來調試程序。有了這些,我應該有足夠的信息可以開始破解了。

        嗯,這些是我的準備工作:

在硬盤留下后門,重裝系統都沒轍

        那個紅色的是一塊 FT2232H 的小板,大概 30 歐元,很便宜,可以用來進行 JTAG 調試,串口,還有 SPI 通信。把它連到硬盤的 JTAG 口,還有串口上。硬盤直接連到我電腦主板的 SATA 口上,還有外部 ATX 電源。我用 OpenOCD 來驅動 JTAG 接口。

        現在的問題是:這玩意真能工作么?Dejan 用的是 88i6745 控制器的 2.5” 250G 硬盤,他檢測到的是 ARM9 內核。我找的是 88i6745 控制器的 3.5” 2TB 硬盤,有不同的格式因素,而且有點新。幸運的是,OpenOCD 可以自動檢測 JTAG 連接的設備。如下所示:

在硬盤留下后門,重裝系統都沒轍

        這我就有點搞不懂了……我本來估計會有一個 tap,就是單獨的 ARM 內核……可這里竟然有三個 tap……難道這個片子有三個 ARM 內核?

        一番研究后,我發現這個芯片真的是有三個內核。兩個是 Feroceon 的內核,是比較牛逼的類似 ARM9 的內核,還有一個是 Crotex-M3 內核,比較小,相比更像微控制器的核心。鼓搗了一陣(以及后來的研究)發現這些控制器各自有不同的功能:

  1. Feroceon 1 處理對磁盤的物理讀寫操作
  2. Feroceon 2 處理 SATA 接口
  3. Feroceon 2 同時處理緩存以及將邏輯塊尋址翻譯成柱面/磁頭/扇區
  4. Cortex-M3 貌似啥都不管?我給他關掉硬盤也沒啥問題。
  5. </ol>

            現在從哪個核心開始破解呢?我的目標是通過使用修改的硬盤固件來影響系統的安全。最簡單的方法,同時也可能是最難檢測的方法就是直接修改數據。 這種方法不需要修改磁盤上的數據,固件可以使自己隱身不可見。為此,我需要找到一個合適的核心來進行監聽:我需要一個能在從硬盤到 SATA 線的傳輸過程中接觸到數據的核心,同時可以被操縱在磁盤和 SATA 線纜之間修改數據。

            現在,數據是如何從硬盤盤片上送到 SATA 借口上的呢?憑黑客的直覺我推測:

            如果處理器工作在 150MHz,使用標準的內存復制,它們就只能達到 150*23/2=2.4Gbp 的速率,而實際情況要比這個少很多。硬盤的速度是 6Gbps,所以肯定有些加速硬件參與其中。最可能的加速硬件應該就是使用 DMA(直接訪問內存)。那就意味著數據直接從磁頭讀回來放進內存,沒有處理器的參與。SATA 口也是一樣:處理器只指明數據在哪里,DMA 會直接從內存中讀數據。

            如果是這樣的話,DMA 引擎指向的內存會在哪呢?硬盤的緩存是個好地方:數據從磁盤讀出來總是要放進緩存的,所以當讀取磁盤的時候馬上去那里復制也就說的通了。我之前發現第二個 Feroceon 負責管理緩存;于是它就成了我的首選目標。

            就這樣,我推斷數據通過 DMA 來讀寫,不需要任何 CPU 動作。現在的問題是:既然 CPU 不會在正常操作中接觸數據,那么 CPU 能不能(非正常地)接觸到數據呢?為了解答這個問題,我首先使用 JTAG 連接,用了一些反匯編,來看看 Feroceon2 號的內存。

    在硬盤留下后門,重裝系統都沒轍

            如你所見,內存圖有些零碎。RAM 中有一些小塊散落著,還有一些 IO 空間和 IRQ 空間,以及一塊內部啟動的 ROM。還有一塊 64MB 的數據段,我猜這個是用作緩存的 DRAM。一起來看看是不是這樣。首先,我把硬盤加載到我的電腦上,在硬盤上的一個文件里寫入「Hello world」。現在看看我是否能從 64MB 的內存中找到這個字符串。

    在硬盤留下后門,重裝系統都沒轍

            沒錯,找到了!看起來 Feroceon2 號可以讀取緩存,并對這塊 64MB 的 DRAM 進行了地址映射。

            注入代碼

            當然了,如果我想要在緩存里修改數據,我可不能每次都完全掃描整個 64MB 的緩存:我需要知道緩存是如何工作的。為此,我需要進行反匯編并理解硬盤的固件,至少要明白緩存的函數。

            對固件進行反匯編,可不是個簡單的活。首先,代碼混合了 ARM 和 Thumb 指令,如果你沒有自動切換兩種指令的反匯編器就很令人抓狂了。而且,沒有那些能使反匯編更簡單的信息了:一般程序都被寫好了,當有東西出錯總會彈出類似 「Couldn’t open logfile!」的信息。這些信息對于了解代碼功能有很大幫助。而這個固件,一條信息都沒有:你得自己看代碼來知道代碼在做什么。代碼庫好像有點老,而 且有些時候反匯編的感覺就像給代碼加了很多特性,把所有事情都搞得更復雜。

            當然,也有幾件事使得反匯編相對簡單些。首先呢,西部數據沒有故意混淆代碼:沒有在指令中間用些跳轉的招數。還有,因為 JTAG 接口的存在,你可以干預代碼的執行,設置斷點,或者直接修改,讓你非常容易地知道程序在做什么。

            我看了很久代碼,試著去理解,有時候用調試器驗證我猜的對不對,最后我找到了緩存系統的核心代碼:在 RAM 中的一個表,我稱之為「緩存描述符表」。

    在硬盤留下后門,重裝系統都沒轍

            緩存描述表的每一項描述了緩存中的一個塊。它包含了可能在緩存中的磁盤扇區的起始 LBA、緩存中存有多少硬盤數據、一些標明了緩存項的狀態標志符,還有一個標明了緩存數據在內存中未知的數。

            現在,緩存描述符表的秘密還沒有被揭開,我能否在數據送出 SATA 口之前截斷磁盤讀取碼?為此,我需要在磁盤控制器上執行我自己的代碼。不僅如此,我還需要確定代碼能否在正確的時間運行:如果它修改緩存太早,數據還沒進去;如果太晚的話,數據已經送到 PC 了。

            我的方法是綁定在一個已存在的任務上。我破解的是 Feroceon2 號,這個 CPU 負責所有的 SATA 傳送,所以肯定有個服務是負責設置 SATA 硬件去緩存中讀取數據。如果我找到這個服務,我就可能在它之前運行我的代碼。

            在看了很多代碼,設置了很多斷點,修改了很多次之后,我最終找到了某個符合條件的服務。我通過連接讓這個服務在執行前先運行我的代碼。這是原來的代碼:

    000167BE ; r0 - slot in sata_req
    000167BE sub_0_167BE:
    000167BE                 PUSH    {R4-R7,LR}
    000167C0                 MOVS    R7, R0
    000167C2                 LSLS    R1, R0, #4 000167C4                 LDR     R0, =sata_req
    000167C6                 SUB     SP, SP, #0x14 000167C8                 ADDS    R6, R1, R0
    000167CA                 LDRB    R1, [R6,#0xD]
    000167CC                 LDR     R2, =stru_0_40028DC
    000167CE                 STR     R1, [SP,#0x28+var_1C]
    000167D0                 LDRB    R0, [R6,#(off_0_FFE3F108+2 - 0xFFE3F0FC)]
    000167D2                 LDRB    R5, [R6,#(off_0_FFE3F108 - 0xFFE3F0FC)]
    000167D4                 LSLS    R0, R0, #4

            這是改成連接到我的代碼之后:

    000167BE ; r0 - slot in sata_req
    000167BE sub_0_167BE:
    000167BE                 PUSH    {R4-R7,LR}
    000167C0                 MOVS    R7, R0
    000167C2                 LD      R6, =hookedAddr
    000167C4                 BX      R6
    000167C6                 .dw     checksumFix
    000167C8                 .dd     hookedAddr
    000167CC                 LDR     R2, =stru_0_40028DC
    000167CE                 STR     R1, [SP,#0x28+var_1C]
    000167D0                 LDRB    R0, [R6,#(off_0_FFE3F108+2 - 0xFFE3F0FC)]
    000167D2                 LDRB    R5, [R6,#(off_0_FFE3F108 - 0xFFE3F0FC)]
    000167D4                 LSLS    R0, R0, #4 ...
    FFE3F000                 PUSH    {R0-R12, LR}
    FFE3F004                 BX      changeThingsInCache
    FFE3F008                 POP     {R0-R12, LR}
    FFE3F00C                 LSLS    R1, R0, #4 FFE3F010                 LDR     R0, =sata_req
    FFE3F014                 SUB     SP, SP, #0x14 FFE3F018                 ADDS    R6, R1, R0
    FFE3F01C                 LDRB    R1, [R6,#0xD]
    FFE3F020                 BX      0x167CC

            如你所見,原來的指令被跳轉到的新代碼替代了,新代碼放在本來沒用到的地址 0xFFE3F000,然后又加了一句,保證代碼域的校驗和有效。如果沒這么做的話,硬盤會嘗試從盤片上讀取備份,那可不是我想要的。跳轉的代碼執行了一 個服務,叫做「changeThingsInCache」然后執行修改代碼本該做的指令。最后接著執行本來的服務好像什么也沒發生過一樣。

            現在我要寫的就是修改緩存數據的服務。首先做個測試,我決定用一個下面的用偽代碼寫的服務:

    void hook () {
      foreach (cache_struct in cache_struct_table) {
        if (is_valid (cache_struct)) {
          foreach (sector in cache_struct.sectors) {
            sector[]=0x12345678;
          }
        }
      }
    }

            這一小段代碼會在每次調用的時候用0×12345678 代替緩存中每個扇區的前四個字節,所以如果我把這個上傳到硬盤的話,我在我看到的每個扇區前面都會看到這個數字。我通過 JTAG 上傳了代碼……

    在硬盤留下后門,重裝系統都沒轍

            然后你看:

    在硬盤留下后門,重裝系統都沒轍

            一勞永逸

            當然了,我可以將固件完全破解,但是每次硬盤啟動都需要用 JTAG 修改 RAM,這就得不償失了。我得讓它保持不變,也就是說,我要把我的修改存在某個地方,每次硬盤啟動都會帶上這段修改程序。

            我選的地方是閃存。我大概也可以放在磁盤本身的保留扇區上,但是如果我一旦弄錯的話,我就沒法恢復我的硬盤了。閃存芯片只是一個八個腳的標準 件,所以我可以輕松地摘下來,刷掉閃存再裝回去。為此,我把它焊下來然后放到萬用板上,這樣我就可以在編程器和硬盤之間輕松切換了。

    在硬盤留下后門,重裝系統都沒轍

            現在,應該在閃存里寫什么呢?很幸運的是,芯片中存儲的格式已經找到了:它包含了多塊數據,還有一個表在最開始描述了這些數據。這個表描述了閃 存中代碼塊的位置,如何壓縮的(如果壓縮了的話),代碼塊應該在放在 RAM 的什么位置,而且在最后的地址中是一個執行指針,標記了啟動器應該跳到什么地方去執行程序。

            不幸的是,我不能修改閃存中的代碼;我想加鉤子的地方的數據被某種不知道的壓縮算法壓縮了,我就不能修改了。然而我能做的是增加一個額外的代碼 塊,修改執行地址這樣這個代碼塊就可以在其他之前執行了。這樣一來就簡單多了。當我的代碼塊執行的時候,我就可以在已經解壓的代碼中加入我的鉤子了。

            當然,我得反匯編,再重編譯閃存的二進制代碼。我為此做了個小工具,非常俗地起名為「fwtool」。這個小工具可以讀出閃存中的很多數據塊, 并把頭翻譯成文本文件以方便修改。接著你就可以修改,刪除或者加上代碼,然后重新編譯成一個固件,準備刷回去。我用它把我的代碼加到鏡像中,再刷回到芯片 里,把芯片裝回硬盤,啟動備份的文件,然后:

    在硬盤留下后門,重裝系統都沒轍

            結果并不新鮮:就是我之前做過的。唯一的變化就是我不用 JTAG 就能辦到了。

            刷軟件

            雖然閃存這邊有了很大進展,我還是不能開始我的黑客腳本:我相信不會有任何一個服務器公司會接受這些帶有反匯編又重匯編的芯片的硬盤。我需要想個辦法能讓芯片不從板子上摘下來就可以刷固件,最好是能直接在硬盤安裝的電腦上刷。

            西部數據的固件升級工具為此提供了可能性:這個工具簡單地在 DOS 環境下的把新固件寫進閃存和服務區——也就是保留扇區。根據網上資料,這個工具使用的所謂「Vendor Specific Commands」命令。也有一些其他的工具可以修改固件:比如,有一種概念驗證性的代碼,可以使用未使用的保留扇區來隱藏數據。最后有一組工具叫做「idle3-tools」 可以通過修改固件中的字節來修改硬盤的閑置行為。這個代碼同樣使用 VSC,通過 Linux 系統的 SCSI(小型計算機系統接口)直通 IOCTLS(輸入輸出控制系統)這種「正式」的途徑來修改代碼。我需要「借用」它的源代碼,修改一下然后整合到我的 fwtool 里面。在胡亂猜了一陣 VSC 參數之后,fwtool 突然可以讀寫電腦上硬盤的閃存芯片了。

            有了這個工具,我的攻擊基本完成了,如果一個黑帽子黑客獲得了一個帶有這樣硬盤驅動器的服務器的最高權限,他就可以使用 fwtool 遠程獲取硬盤閃存,修改然后刷回去。最終,主機的主人會發現我用他的主機為非作歹,然后可能會重裝系統,斷掉黑客原來進入主機的路。

            但是有了這個破解了的固件,攻擊者可以操縱硬盤在新安裝的系統里繼續為非作歹。首先他需要觸發行為,這需要事先在硬盤里寫入一個破解固件需要的 某個特定字符串。這個字符串可以在任何一個文件中:攻擊者可以向服務器上傳一個帶有代碼的 .jpeg 文件。他也可以通過向服務器發送在 URL 中追加了特定代碼的文件請求來實現。這最終會在服務器的記錄文件中結束,觸發利用。

            接下來,被破解的硬盤固件就開始搗亂了。比如,他會等待主機讀出/etc/shadow 中的文件,其中存儲了 Unix/Linux 的所有密碼,然后立即修改成攻擊者之前寫進去的一些東西。當攻擊者之后嘗試用他自己的密碼登入系統的時候,主機會根據修改過的/etc/shadow 判斷密碼,攻擊者就可以再次輕松登錄。

            這是我做的演示。你可以看見我沒能成功登錄主機的根用戶。然后我啟動破解,給它一個代替密碼的哈希值,也就是密碼「test123」。因為 Linux 系統把影子文件緩存了(如同所有最近存取的文件),我需要制造很多硬盤活動把緩存清出去;這樣,當我再次登錄的時候,Linux 系統會再次讀取磁盤上的影子文件。最終,緩存已清空,我可以用假的「test123」密碼登錄根用戶了。

            其他用法

            當然了,恢復服務器中清除的隱秘登錄方法并不是我研究成果的唯一用法。這同樣可以用于防御目的。

            例如,你可以做一個不可復制的硬盤:如果扇區的讀取模式是隨機的話,像正常的操作系統讀取文件系統,硬盤會正常工作。如果硬盤是有序的讀取,像硬盤復制設備那樣的話,硬盤會篡改數據,無法復制出原來的內容。

            硬盤控制器作為一個通用控制器也是有的玩的。你手里的是三個性能不錯的 CPU 核心,連著一個相當大的 RAM。還有一個 UART 作為串口,至少兩個 SPI 接口:一個連接到閃存 ROM,一個連到電機控制器。你可以通過升級外部閃存芯片來給處理器加載代碼,或者甚至在啟動加載器上用串口加載。為了演示芯片的能力,我在硬盤上移植了 一個相當普及的軟件。這個 demo 只是概念驗證性的,串口是唯一工作的外圍設備,而且沒有用戶空間。雖然如此, 我還是很驕傲的宣稱我在我的硬盤控制器上裝了一個 Linux。在頂端,是標準的命令行(硬盤加載在/mnt 下),低端是我在硬盤串口上的輸出。

            在此多解釋一下這是怎么工作的:內核和啟動都封裝成每塊大小都是一個扇區的一個個的包,包的前面帶有特殊字符串和編號數字。通過從磁盤讀取數 據,內核和啟動最終會進入緩存。寫入特殊字符串「HD, Inx!」最終觸發了修改過的固件,在緩存中搜索所有扇區,重編譯內核然后啟動。但是一個沒有內存控制單元的內核也需要特殊格式的用戶空間。我不能把這個 也編譯了,所以內核最終因為找不到 init 來執行而崩潰。

            結論

            是的,就是這樣。雖然硬盤控制器如同一個不知其究竟的野獸,它仍能通過逆向工程加以了解,并為其寫出代碼以執行。對控制器的未知,使得通用破解 充滿難度,令我懷疑這東西是不是永遠不會出現一個惡意的固件補丁:相比對每個服務器的每個硬盤固件進行逆向工程加以破解,還是找一個 0day 漏洞更加簡單吧。

            我還希望證實一個壞掉的硬盤仍然能夠使用。當硬盤的機械部分壞掉的時候,PCB 仍然帶有可用的嵌入式系統,其性能相當不俗,尤其是壞的硬盤基本都不要錢就能拿到。

            開放安全工程的源代碼什么的太惡劣了。我想開放代碼,但是我不想為由此產生的大量的「永久破解」的服務器負責……我決定做個妥協:你可以在這里下載代碼,但是我移除了影子替代部分的代碼。注意:反正我不負責讓整個過程完全可運行;黑客,你自己來吧。

      英文原文:Hard disk hacking 

      譯者:@心理未成年的某青年這次要給力 譯文鏈接

     本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
     轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
     本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!