隨筆-3  評論-0  文章-8  trackbacks-0

          附件[462666-gifs.rar]
          http://blog.blogchina.com/upload/2004-12-25/20041225191140927794.rarLinux 啟 動 分 析 專 題   

            第一部分 背景知識簡介

            幾乎所有編寫代碼的人都有這種體會:如今在計算機這個行業中,許多技術不是你不懂,而是你不知道。所以,在分析之前有些背景知識是必須要知道的。

            一. 硬盤結構簡介

            1. 硬盤參數釋疑

            到目前為止, 人們常說的硬盤參數還是古老的 CHS (Cylinder/Head/Sector)參數. 那么為什么要使用這些參數, 它們的意義是什么? 它們的取值范圍是什么?
            很久以前, 硬盤的容量還非常小的時候, 人們采用與軟盤類似的結構生產硬盤,也就是硬盤盤片的每一條磁道都具有相同的扇區數,由此產生了所謂的3D參數 (Disk Geometry)。既磁頭數(Heads), 柱面數(Cylinders), 扇區數(Sectors),以及相應的尋址方式。

            其中:
            磁頭數(Heads) 表示硬盤總共有幾個磁頭,也就是有幾面盤片, 最大為 255 (用 8 個二進制位存儲);
            柱面數(Cylinders) 表示硬盤每一面盤片上有幾條磁道, 最大為 1023(用 10 個二進制位存儲);
            扇區數(Sectors) 表示每一條磁道上有幾個扇區, 最大為 63 (用 6個二進制位存儲);
            每個扇區一般是 512個字節(理論上講這不是必須的, 但好象都取此值)。

            據此,磁盤最大容量為:
            255 * 1023 * 63 * 512 / 1048576 = 8024 MB ( 1M = 1048576 Bytes )
            或硬盤廠商常用的單位:
            255 * 1023 * 63 * 512 / 1000000 = 8414 MB ( 1M = 1000000 Bytes )

            在 CHS 尋址方式中, 磁頭, 柱面, 扇區的取值范圍分別為 0 到 Heads - 1,0 到 Cylinders - 1, 1 到 Sectors (注意是從 1 開始)。

            2. 基本 Int 13H 調用簡介

            BIOS Int 13H調用是 BIOS 提供的磁盤基本輸入輸出中斷調用, 它可以完成磁盤(包括硬盤和軟盤)的復位, 讀/寫, 校驗, 定位, 診斷, 格式化等功能。它使用的就是 CHS 尋址方式, 因此最大只能訪問 8 GB 左右的硬盤 ( 本文中如不作特殊說明, 均以 1M = 1048576 字節為單位).
            而更不幸的是,標準的IDE接口容許256個扇區/磁道、65536個柱面及16個磁頭。它自己本身可以存取 137438953472(128 GB),但是加上BIOS方面63個扇區與1024個柱面的限制后,就只剩528482304(1024*16*63 = 504MB)可以定址得到,這就是所謂標準IDE硬盤只認前504MB問題。

            3. 現代硬盤結構簡介

            在老式硬盤中, 由于每個磁道的扇區數相等 (與軟盤一樣), 所以外道的記錄密度要遠低于內道, 因此會浪費很多磁盤空間。為了解決這一問題, 進一步提高硬盤容量, 人們改用等密度結構生產硬盤, 也就是說, 外圈磁道的扇區比內圈磁道多。采用這種結構后, 硬盤不再具有實際的3D參數, 尋址方式也改為線性尋址, 即以扇區為單位進行尋址。
            為了與使用3D尋址的老軟件兼容 (如使用BIOS Int13H接口的軟件), 在硬盤控制器內部安裝了一個地址翻譯器, 由它負責將老式3D參數翻譯成新的線性參數。這也是為什么現在硬盤的3D參數可以有多種選擇的原因 (不同的工作模式對應不同的3D參數, 如 LBA, LARGE, NORMAL)。

            4. 擴展 Int 13H 簡介

            雖然現代硬盤都已經采用了線性尋址, 但是由于基本 Int 13H 的制約, 使用 BIOS Int 13H 接口的程序, 如 DOS 等還是只能訪問 8 G 以內的硬盤空間。為了打破這一限制, Microsoft 等幾家公司制定了擴展 Int 13H 標準(Extended Int13H,詳見附錄A), 采用線性尋址方式存取硬盤,所以突破了 8 G 的限制,而且還加入了對可拆卸介質 (如活動硬盤) 的支持。

            二. Boot Sector 結構簡介

            1. Boot Sector 的組成

            Boot Sector 也就是硬盤的第一個扇區, 它由 MBR (Master Boot Record),DPT (Disk Partition Table) 和 Boot Record ID(Magic Number) 三部分組成。

            MBR 又稱作主引導記錄,占用 Boot Sector 的前 446 個字節 ( 0 to 0x1BD ),包含了硬盤的一系列參數和一段系統主引導程序。引導程序主要是用來在系統硬件自檢完后負責從活動分區中裝載并運行系統引導程序(引導操作系統)。它的最后一條執行語句是一條JMP指令,跳到操作系統的引導程序去。這里往往是引導型病毒的注入點,也是各種多系統引導程序的注入點。但是由于引導程序本身完成的功能比較簡單,所以我們完全可以判斷該引導程序的合法性(比如看JMP指令的合法性),因而也易于修復。象命令fdisk/mbr可以修復MBR和KV300這類軟件可以查殺任意類型的引導型病毒,就是這個道理。
            DPT 即主分區表,占用 64 個字節 (0x1BE to 0x1FD),記錄了磁盤的基本分區信息。主分區表分為四個分區項, 每項 16 字節, 分別記錄了每個主分區的信息(因此最多可以有四個主分區)。
            Boot Record ID 即引導區標記,占用兩個字節 (0x1FE and 0x1FF), 對于合法引導區, 它等于 0xAA55, 這是判別引導區是否合法的標志.
            Boot Sector 的具體結構如下圖所示:

            

            2. 主分區表的結構

            主分區表由四個分區項構成, 每一項的結構如下:

            BYTE State : 分區狀態, 0 = 未激活, 0x80 = 激活 (注意此項)
            BYTE StartHead : 分區起始磁頭號
            WORD StartSC : 分區起始扇區和柱面號, 低字節的低6位為扇區號,高2位為柱面號的第 9,10 位, 高字節 為柱面號的低 8 位
            BYTE Type : 分區類型, 如 0x0B = FAT32, 0x83 = Linux 等, 00 表示此項未用
            BYTE EndHead : 分區結束磁頭號
            WORD EndSC : 分區結束扇區和柱面號, 定義同前
            DWORD Relative : 在線性尋址方式下的分區相對扇區地址 (對于基本分區即為絕對地址)
            DWORD Sectors : 分區大小 (總扇區數)

            注意:在 DOS / Windows 系統下, 基本分區必須以柱面為單位劃分( Sectors * Heads 個扇區), 如對于 CHS 為 764/255/63 的硬盤, 分區的最小尺寸為 255 * 63 * 512 / 1048576 = 7.844 MB。

            3. 擴展分區簡介

            由于主分區表中只能分四個分區, 有時無法滿足需求, 因此設計了一種擴展分區格式。 基本上說, 擴展分區的信息是以鏈表形式存放的, 但也有一些特別的地方。
            首先,主分區表中要有一個基本擴展分區項, 所有擴展分區都隸屬于它,也就是說其他所有擴展分區的空間都必須包括在這個基本擴展分區中。 對于DOS / Windows 來說, 擴展分區的類型為 0x05。
            除基本擴展分區以外的其他所有擴展分區則以鏈表的形式級聯存放, 后一個擴展分區的數據項記錄在前一個擴展分區的分區表中, 但兩個擴展分區的空間并不重疊。
            擴展分區類似于一個完整的硬盤, 必須進一步分區才能使用。但每個擴展分區中只能存在一個其他分區, 此分區在 DOS/Windows 環境中即為邏輯盤。因此每一個擴展分區的分區表 (同樣存儲在擴展分區的第一個扇區中)中最多只能有兩個分區數據項(包括下一個擴展分區的數據項)。
            擴展分區和邏輯盤的示意圖如下:
            
            
            

            
            


            三. 系統啟動過程簡介

            系統啟動過程主要由一下幾步組成(以硬盤啟動為例):

            1. 開機;
            2. BIOS 加電或按reset鍵后都要進行系統復位,復位后指令地址為 0ffff:fff0,這個地方只有一條JMP指令, 跳轉到系統自檢 ( Power On Self Test -- POST )程序處;
            3. 系統自檢完成后,將硬盤的第一個扇區 (0頭0道1扇區, 也就是Boot Sector)讀入內存地址 0000:7c00 處;
            4. 檢查 (WORD) 0000:7dfe 是否等于 0xaa55, 若不等于則轉去嘗試其他啟動介質, 如果沒有其他啟動介質 則顯示 "No ROM BASIC" 然后死機;
            5. 跳轉到 0000:7c00 處執行 MBR 中的程序;
            6. MBR程序 首先將自己復制到 0000:0600 處, 然后繼續執行;
            7. 在主分區表中搜索標志為活動的分區,如果沒有發現活動分區或有不止一個活動分區, 則轉停止;
            8. 將活動分區的第一個扇區讀入內存地址 0000:7c00 處;
            9. 檢查 (WORD) 0000:7dfe 是否等于 0xaa55, 若不等于則 顯示 "Missing Operating System" 然后停止, 或嘗 試軟盤啟動或;
            10. 跳轉到 0000:7c00 處繼續執行特定系統的啟動程序;
            11. 啟動系統...

            以上步驟中 2,3,4,5 步是由 BIOS 的引導程序完成. 6,7,8,9,10步由MBR中的引導程序完成.

            一般多系統引導程序 (如 SmartFDISK, BootStar, PQBoot 等)都是將標準主引導記錄替換成自己的引導程序, 在運行系統啟動程序之前讓用戶選擇要啟動的分區。
            而某些系統自帶的多系統引導程序 (如 lilo, NT Loader 等)則可以將自己的引導程序放在系統所處分區的第一個扇區中, 在 Linux中即為 SuperBlock (其實 SuperBlock 是兩個扇區)。

            注:以上各步驟中使用的是標準 MBR, 其他多系統引導程序的引導過程可能與此不同。

            下面簡要說明一下系統復位后的指令地址0ffff:fff0(物理地址0x0fffffff0):
            在實地址模式下,內存有兩個保留區域:系統初始化區和中斷向量表區。地址0x00000~0x003ff 是為中斷向量保留的,256個可能的中斷,每一個保留4字節的跳轉向量;地址0xfffffff0~0xffffffff是為系 統初始化保留的,此處一般只有一條JMP指令,跳到系統初始引導程序。
            系統復位后,cs = 0x0f000、eip = 0x0000fff0,而系統初始引導程序安排在 0x0ffff0000~0x0ffffffff, 一般為ROM固件,使初始引導程序工作于內存實際地址空間以外的另一存儲段中。此區域的16~31位都 應為1,cs及eip的初值已保證地址線的16~19位為1,而20~31位,即地址線的高12位,則須由硬件強制置 1,這由一個標志觸發器在系統每次復位時置位實現觸發。而由于段間轉移指令要重新裝入cs寄存器, 因此,每當執行段間轉移指令時,此標志觸發器復位,以后,再次訪問時,不再向高12位地址線提供"1"信號,程序從此正常地工作于前1MB的地址空間。


            第二部分 硬盤MBR主引導代碼分析

            一.程序流程

            (引導扇區是指硬盤相應分區的第一個扇區,是和操作系統有關的,操作系統的引導是由它來完成的;而MBR主引導程序并不負責引導操作系統,MBR是和操作系統無關的,他的任務是把控制權轉交給操作系統的引導程序.)

            1 將程序代碼由0:7C00H移動到0:0600H(注,BIOS把MBR放在0:7C00H處)
            2 搜索可引導分區,即80H標志
            成功:goto 3
            失敗:跳入ROM BASIC
            無效分區表:goto 5
            3 讀引導扇區
            失敗:goto 5
            成功:goto 4
            4 驗證引導扇區最后是否為55AAH
            失敗:goto 5
            成功:goto 6
            5 打印錯誤進入無窮循環
            6 跳到0:7C00H進行下一步啟動工作

            二.代碼注釋

            下面將用匯編語言寫出這一段代碼,并進行說明。

          ;MBR.ASM
          ; MASM MBR
          ; LINK MBR
          ; EXE2BIN MBR

          .MODEL tiny
          .CODE

            ;設置寄存器及堆棧值

          org 0
          Head:
          Start:
          cli
          xor ax,ax
          mov ss,ax
          mov sp,7C00H ;ss:sp=0:7C00H
          mov si,sp
          push ax
          pop es
          push ax
          pop ds ;es=ds=0
          sti

            ;將程序代碼由0:7C00H移動到0:0600H處

          cld
          mov di,600H
          mov cx,100H ;100H Words=512 Bytes,即一個扇區大小
          repne movsw
          db 0EAH ;這個是FAR JUMP的機器碼
          dw offset Continue+600H, 0000H ;這個是跳轉目的地址,即0:061DH

            ;搜索可引導分區

          Continue:

          mov si,600H+1BEH ;si指向分區表
          mov bl,4 ;四個分區

          FindBoot:

          cmp byte ptr[si],80H
          je SaveRec ;讀扇區位置
          cmp byte ptr[si],0
          jne Invaild ;無效分區
          add si,10H
          dec bl
          jnz FindBoot
          int 18H ;進入ROM BASIC

            ;讀取引導分區的扇區,柱面號

          SaveRec:

          mov dx,[si]
          mov cx,[si+2]
          mov bp,si

            ;檢查其余分區表

          FindNext:

          add si,10H
          dec bl
          jz SetRead
          cmp byte ptr[si],0 ;是否存在非法分區
          je FindNext

          Invaild:

          mov si,offset ErrMsg1+600H

            ;字符串輸出子程序

          PrintStr:

          lodsb
          cmp al,0
          je DeadLock
          push si
          mov bx,7
          mov ah,0EH ;輸出字符
          int 10H
          pop si
          jmp short PrintStr ;下一字符

          DeadLock:

          jmp short DeadLock ;無窮循環,也可以寫成jmp $

            ;讀引導扇區

          SetRead:

          mov di,5 ;讀取次數

          ReadBoot:

          mov bx,7C00H
          mov ax,201H
          push di
          int 13H ;cx,dx已經在SaveRec處得到
          pop di
          jnc GoBoot ;成功則啟動
          xor ax,ax
          int 13H ;reset驅動器,然后再讀取
          dec di
          jnz ReadBoot

          mov si,offset ErrMsg2+600H
          jmp short PrintStr 失敗輸出信息,并進入無窮循環

            ;檢查讀入的引導扇區

          GoBoot:

          mov si,offsetErrMsg3+600H
          mov di,7C00H+1FEH
          cmp word ptr[di],0AA55H
          jne PrintStr ;非AA55標志則輸出錯誤信息
          mov si,bp ;si指向可啟動分區
          db 0EAH,0,7CH,0,0 ;跳轉至0:7C00H

          ErrMsg1 db 'Invaild partition table',0
          ErrMsg2 db 'Error loading operating system',0
          ErrMsg3 db 'Missing operating system',0

          Tail:

          FillNum equ 1BEH-(Tail-Head) ;計算填0數目
          db FillNum dup(0)

            ;四個分區表項數據,跟分區情況有關,詳細含義另解

          PartTable db 80H,1,1,0,4,4,0D1H,2,11H,0,0,0,0FEH,0FFH,0,0
          db 0,0,0C1H,3,5,4,0D1H,0FEH,0FFH,0FFH,0,0,0ACH,53H,0,0
          db 20H dup(0)

          ID dw 0AA55H

          end start

          ;如果開始試用org 600H,那么訪問數據時就不必加上600H,如mov si,offset
          ErrMsg2+600H
          ;可寫為mov si,offset ErrMsg2,這時就不能用exe2bin得到數據,必須試用debug
          ;debug mbr.exe
          ;-nmbr.bin
          ;-rcx 200
          ;-wcs:600
          ;-q

            在硬盤的第一個扇區上保存著分區信息,即主分區表,共有四項,讀取分區表必須使用bios的int 13h,一般使用debug就可以了:

          debug
          -a
          xxxx:0100 mov ax,201
          mov bx,200
          mov cx,1
          mov dx,80 ;如果是第二個硬盤則是81...
          int 13
          int 20
          xxxx:????
          -g=100

            這時xxxx:0200開始的512字節就是分區表所在的扇區,前面一部分為MBR,在debug中用-d3be l40就可以看到64字節的分區表信息,16個字節為一項,用-e命令就可以修改,改完后可以重新寫回去,只要把前面代碼中的mov ax,201改為mov ax,301即可,或者直接把102處的2改成3,比如:

          -e 102
          xxxx:0102 02.3
          -g=100

            這樣就寫回去了,不過修改硬盤的第一個扇區須非常謹慎。

            下面說一下分區表項的具體意義,取其中一項舉個例子:

            80 01 01 00 0B 3F FF 00 3F 00-00 00 81 4F 2F 00

            1 (80)引導標志,80代表可引導,00代表不可引導,一般必須且只能有一個分區表項的引導標志為 80,除非你自己修改MBR
            2 (01)分區開始磁頭
            3,4 (01 00)=(0,1)分區開始柱面和扇區(后面后詳解)
            5 (0B)分區類型(后面有詳解)
            6 (3F)=(63)分區結束磁頭
            7,8 (FF 00)=(768,63)分區結束柱面和扇區(同上)
            9-12 (3F 00 00 00)=(63)此分區前扇區總數,即相對扇區數
            13-16 (81 4F 2F 00)=(002F4F81H=3100545)此分區扇區總數

            柱面和扇區共用兩個字節表示,而柱面號為10位,最大1023,扇區號為6位,最大63,具體各位分布如下圖:
                     扇區號
                   _____|____
                    |   |
            ( 7 6 5 4 3 2 1 0 ) ( 7 6 5 4 3 2 1 0 )
             |__|         |___________|
              |___________________|
                   |
                  柱面號

            關于分區類型,常見的有:

            00 未用,Unused
            01 DOS-12(FAT 12)
            02 XENIX
            04 DOS-16(FAT 16)(分區<32M的,應該已沒有了)
            05 EXTEND(DOS擴展分區)
            06 BIGDOS(>32M)(這個才是現在常說的FAT 16)
            07 HPFS(OS/2)(NTFS也是這個標記,好像是)
            0B FAT 32
            0F 這個一時不確定
            50 DM
            63 386/ix(unix)
            64 NET286(Novell)
            65 NET386(Novell)
            82 Linux swap
            83 Linux native
            FF BBT(UNIX Bad Block Table)

            下面有幾個算式用來計算分區參數:

            1)第一分區參數

            扇區總數=(結束柱面+1)*磁頭數*每柱面扇區數-相對扇區數,例如:3100545=(768+1)*64*63-63

            2)其它分區參數

            扇區總數=(結束柱面-起始柱面+1)*磁頭數*每柱面扇區數,如下例:
            00 00 C1 01 05 3F FF FD C0 4F-2F 00 C0 90 0F 00
            000F90C0H=1020096,(FF FD)=(1021,63),(C1 01)=(769,1),1020096=(1021-769+1)*64*63

            3)第一分區相對扇區=每柱面扇區數
            其它分區相對扇區=上一分區相對扇區+上一分區扇區總數

            擴展分區信息是一個鏈狀結構,在刪除分區時,把前面的分區刪掉會導致后面的分區也找不到,原因就在于此,我們從主分區表中取出擴展分區項進行一下分析,如下:

            00 00 01 C0 05 FE BF 6E C0 10-2F 00 EF A6 69 00

            由此我們可以得到數據:
            開始磁頭:00 
            開始柱面扇區:01 C0=(192,1)

            用debug

            debug
            -a100
            xxxx:0100 mov ax,201
            mov bx,200
            mov cx,c001 ;開始柱面扇區號
            mov dx,80 ;dh中為開始磁頭號,這里為0
            int 13
            int 20
            xxxx:????
            -g=100
            -d3be l10

            讀出的扇區中有兩個16字節的分區表項和最后的一個55AA標志,這兩個分區表項為:

            00 01 01 C0 06 FE 7F 97 3F 00-00 00 99 F2 34 00
            00 00 41 98 05 FE BF 6E D8 F2-34 00 17 B4 34 00

            第一個分區類型為6,其實這是第一個邏輯分區,含義和主分區表項相同
            第二個分區類型為5,這其實是指向下一個擴展分區表的
            從這里我們可以得到:
            開始磁頭:0
            開始柱面扇區:41 98=(408,1)

            繼續用debug讀出(mov cx,9841)得到

            00 01 41 98 06 FE BF 6E 3F 00-00 00 D8 B3 34 00

            只有一個表項,是第二個邏輯盤,而且是邏輯盤鏈的最后一個。
            可以看到,主分區表是非常重要的,所以除了小心操作外,還應當進行備份,最安全的備份方式就是用筆抄下來,當然,每次重新進行分區后還應當及時更新。從前面可以看出,分區表里最重要的還是柱面號,其它比如磁頭號都是0或者1或者最大值,扇區號都是1或63(最大值),扇區總數什么的也都能算出來,所以分區時最好把各分區的柱面號記下來,這樣一旦分區信息被破壞就可以進行恢復了。
            如果主分區表不幸丟失或者邏輯分區鏈被破壞,那么只要從硬盤上找出還存在的分區信息,就有可能部分恢復分區信息,甚至全部可以恢復,不過這樣的麻煩大家還是應盡量避免。
            在Linux里有一種方法可以恢復主引導扇區(包括主分區表),用如下的命令:
            dd if=/boot/boot.NNNN of=/dev/hda bs=512 count=1
            其中:boot.NNNN 是我們在安裝Linux之前整個主引導扇區的備份,NNNN是分區的主次設備號;bs(buffer size)是指重寫的字節數。如只是為了修復主引導記錄MBR(比如,想把LILO卸載掉),而不是恢復整個主引導扇區,則用如下的命令:
            dd if=/boot/boot.NNNN of=/dev/hda bs=446 count=1
            只把主引導扇區的備份文件boot.NNNN的前446個字節重寫入主引導扇區。

          posted on 2005-08-24 10:58 Smokingcat 閱讀(94) 評論(0)  編輯  收藏

          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          主站蜘蛛池模板: 赣榆县| 安泽县| 黄山市| 大姚县| 怀柔区| 合川市| 安溪县| 定结县| 顺昌县| 农安县| 南溪县| 青海省| 承德县| 惠安县| 玛曲县| 长丰县| 延吉市| 沾化县| 惠州市| 遂宁市| 临武县| 泉州市| 花莲县| 高碑店市| 客服| 鄯善县| 沂源县| 金秀| 毕节市| 仙桃市| 霍林郭勒市| 马边| 土默特左旗| 囊谦县| 忻城县| 乌拉特前旗| 黔南| 宣城市| 怀化市| 伊宁县| 江源县|