转载:pci

来源:互联网 发布:求实软件 编辑:程序博客网 时间:2024/06/07 00:19

PCI的基本常識

首先介紹PCI的基本常識, PCI Bus是在約1990年由Intel發展出來, 用來連接主機板上的各項裝置的匯流排標準, 後來成為業界的標準之一, Spec可以在PCI-SIG註 冊會員之後下載, 架構上簡單的說, 就是一個Host bridge, 在一般的PC上通常指的就是North Brdige, 這個brdige後面就是bus#0(當然也有Multi Host-Bridge的狀況, 這邊舉例的是最單純的情況), 然後接到South Bridge, South Bridge之後可能接的是ISA Bus, IDE Controller, USB, IDE, DMA Controller等等, 如果bus#0上還有別的PCI Bridge, 這個Bridge後面就是bus#1, 如果有多個Bridge存在(PCI最多可以有256個bus), bus#就不一定是固定的了, 一個PCI Bus上可以有32個device, 每個device可以有8個function, 每個function都有屬於自己的256個register. 在PCI的規範裡, 256個register中的前0×40個是公定的功能, 從0×40到0xff則由各家廠商自行實作, 存取這些register的方法, 一般的PC上是透過IO port 0xCF8~0xCFF, 如果是新的PCI-Express則是直接透過Memory Mapped IO, 以存取記憶體的方式直接進行存取, 例如我們想要讀取一個在bus#0 dev#1 func#0的register#40~43, 透過IO的方式如下:

mov eax, (0x01 << 31 ) // Type-1 PCI Configuration
+ (0x00 << 16 ) // Bus#0
+ (0x01 << 11 ) // Device#1
+ (0x00 << 8 ) // Function#0
+ 0x40 // Register#40
mov dx, 0xCF8 // Index Port 0xCF8 ~ 0xCFB
out dx, eax
mov dx, 0xCFC // Data Port 0xCFC ~ 0xCFF
in eax, dx // Get Data in EAX

如果是透過MMIO的方式則簡單多了:

mov esi, (MMIO_BASE) + (0x00 << 20) + (0x01 << 15) + (0x00 << 12) + 0x40
mov eax, [esi]

今天我們比較有興趣的是位在0×30~0×33的register, 在PCI Spec定義中, 這裡的值存放的就是expansion rom在physical memory中被decode到IO的位址, 比如說這個位址是0xFE000000, 如果你在0×30把bit0設為0×1(io->mem decode), Command Register(0×04~0×05)的Memory Space Bit打開, 在0xFE000000的地方你就可以找到這個rom, 開頭是0×55AA(這當然也是規範之一, 用來辨視是否為一個PCI rom), BIOS在POST過程中, 會逐一掃描位於主機板上所有的PCI Device, 假如device上有rom, 就會把它給拷貝到memory中, 然後用jmp指令跳到ROM開頭offset 0×02(別忘了開頭offset 0×00是0×55AA)的地方開始執行PCI ROM, 執行完後ROM會再把控制權交回到BIOS手上, 一般而言, 在傳統記體空間中, 0xC0000~0xCFFFF是給VGA ROM用的, 0xD0000~0xEFFFF則是留給一般的PCI ROM使用, 當然各種情況下還是會有些許差異, 例如有些BIOS會保留0xE0000~0xFFFFF給自己使用, 像是BIOS的interrupt service, DMI data….等等雜七雜八的東西, 執行完的ROM仍然會保留在記憶體中, 因為有些ROM會修改IVT(Interrupt Vector Table), 將某些interrupt service導向自己的code, 像是VGA ROM可能就會hook int 0×10, 這是很合理的, 因為int 0×10是BIOS所提供用來控制螢幕的service, 聰明的你看到這裡應該就知道前面所提的那篇文章想說什麼了, 如果有個”惡意”的程式被埋在PCI ROM裡面, 只要一開機就會自動被執行, 它的運作並不是一時的, 它可以hook某個OS一定會用到的BIOS Interrupt Service, 然後在這個interrupt service被呼叫的時候動作就可以了, 而且麻煩的是即使你format你的HDD也沒用, 除非你把有問題的PCI Device從你的主機板上移除, 嗯….聽起來蠻炫的, 但是, 有可能嗎?

要回答這個問題之前, 需要知道一些基本的常識, 在保護模式下, 因為IO動作受到限制的關係, 要存取IO並不像在DOS那樣容易, 但如果想嘗試Re-flash一顆PCI ROM, 勢必得進行IO動作, 所幸在Windows下這並不是不可能的事, 有些人可能知道利用SeTcbPrivilege和使用ProcessUserModeIOPL structure呼叫undocumented Native API NtSetInformationProcess()就可以達成目的. 一旦攻擊者有辦法修改PCI ROM, 他就可以利用文章中所提到的例子: int 0×10(Windows在開機過程中會透過Ke386CallBios()呼叫int x010), 作他想作的任何事了.

可惜不論哪種攻擊方式, 最終都是要對OS kernel動手腳, 利用各種偵測工具(如: Archon Scanner), 一定可以找到有問題的地方, 如果最後的箭頭指向PCI ROM, 我們可以透過上文所述, 將存在PCI Device和memory中的ROM給dump出來, 需要dump兩邊的rom, 是因為PCI Spec規範中允許實際上所需要配置的記憶體不一定要等於原本rom的大小, 藉以節省保貴的記憶體(別忘了PCI ROM只能被配置到幾個64KB的segment裡而已), 然後向PCI Device的製造商索取正常版本的rom進行比對, 藉以得知是否為被修改的版本. 假如發現有不一樣的地方, 接下來可以朝幾個方向繼續分析是否為有問題的ROM, 我們可以檢查一下它是否修改了不必要的IVT, 像PXE ROM就不太可能hook int 0×10, 或是有保護模式相關的程式碼, 因為一般的ROM應該都是在real mode下執行, 所以應該不會切到protected mode, 如果有相關的程式碼那就非常可疑, 還有rom裡面是否有可疑的字串, 或是位在Windows Kernel位址空間裡的32-bit address, 另外ROM裡面也不太可能出現編碼過的code, 如果rom裡的code很難被disassemble, 或是充滿了一堆obfuscated code, 那也是很有問題.

除了軟體的方式, 最近興起的新技術TPM也能克制這種攻擊手法, 關於TPM詳細的運作, 就留到下次吧.