《Windows內核安全與驅動編程》-第九章-磁盤的虛擬-day-2

磁盤的虛擬

9.4 EvtDriverAdd 函數

9.4.5 用戶配置的初始化

​ 當前,根據上一次的學習內容,設備和用來處理設備請求的隊列都建立好了。接下來初始化與內存盤相關的一些數據結構。內存盤在驅動中的代表就是剛剛創建的設備,內存盤對應的數據結構自然應該與創建的這個設備相關。所以我們在設備拓展中存放這些數據結構。其中 DISK_INFO 用來給用戶提供一些可配置的參數。

typedef struct _DEVICE_EXTENSION{
    // 用來指向一塊內存區域,作爲內存盤的實際數據存儲空間
    PUCHAR DiskImage;
    // 用來存儲內存盤的磁盤 Geometry
    DISK_GEOMETRY DiskGeometry;
    // 我們自己定義的磁盤信息結構,在安裝時候存放在註冊表中
    DISK_INFO  DiskRegInfo;
    // 這個盤的符號鏈接名,這是真正的符號鏈接名
    UNICODE_STRING SysbolicLink;
    // DiskregInfo 中 DriverLettr 的存儲空間,這是用戶在註冊表中指定的盤符
    WCHAR	DriverLetterBuffer[DRIVER_LETTER_BUFFER_SIZE];
    //SymbolicLink 的存儲空間
    WCHAR	DosDeviceNameBuffer[DOS_DEVNAME_BUFFER_SIZE];
}DEVICE_EXTENSION,*PDEVICE_EXTENSION;

typedef sturct _DISK_INFO{
    //磁盤的大小,以byte 計算,我們的磁盤最大設爲4GB
    ULONG DiskSize;
    //磁盤上根文件系統的進入節點
    ULONG RootDirEntries;
    //磁盤上的每個簇由多少個山區組成
    ULONG SectrosPerCluster;
    //磁盤的盤符
    UNICODE_STRING DriverLetter;
}DISK_INFO,*PDISK_INFO;

​ 在瞭解了數據結構中各個成員對象的用處之後,就可以開展對這些數據結構的初始化工作了。首先要去註冊表中獲取用戶指定的信息。這裏通過自定義的函數實現。

//將生成的設備的設備拓展中相應的 UNICODE_STRING 初始化
pDeviceExtension->DiskRegInfo.DriverLetter.Buffer = (PWSTR)pDeviceExtension->DriverLetterBuffer;
pDeviceExtension->DiskRegInfo.DriverLetter.MaximumLength = sizeof(pDeviceExtension.DriverLetterBuffer);
//從系統爲本驅動提供的註冊表鍵中獲取我們需要的信息
RamDiskQueryDiskRegParameters(
	WdfDriverGetRegistryPath(WdfDeviceGetDriver(device));
	&pDeviceExtension->DiskRegInfo
);

​ 首先使用 WdfDeviceGetDriver 獲取我們生成的設備的上層驅動對象,然後使用 WdfDriverGetRegistryPath 從驅動對象中獲取到相應的註冊表路徑。第二個參數是一個結構體變量,在這裏將要向這個結構體變量裏填寫從註冊表獲得的值。

​ 獲取了註冊表路徑之後,還需要分配一定大小的空間來模擬磁盤空間。這個大小是由註冊表中的磁盤大小參數來決定的,往後這塊空間稱爲 Ramdisk 的磁盤鏡像。在這裏,我們全部分配非分頁內存,關於分頁和非分頁內存是在操作系統裏學習到的知識。

​ 在分配了內存之後,磁盤就有了存儲空間,但是就好像任何新磁盤一樣。這個磁盤需要被分區,格式化。需要我們自己去格式化操作,因爲內核中是沒有地方調用 format 命令的。具體的操作會在下一節介紹。這裏先知道 RamDiskFormatDisk 起的作用是把內存介質的磁盤格式化就可以了。

9.4.6 鏈接給應用程序

​ 至此,鍵盤設備已經具備了所有的部件,最後需要做的是暴露給應用層以使用。在 Windows 中各個盤符 C: D: 實際上都是符號鏈接。應用層的代碼不能直接訪問在內核中建立的設備,但是可以訪問符號鏈接,所以在這裏只需要用符號鏈接指向這個設備,就可以訪問這個設備了。這裏我們根據用戶配置中選定的盤符去建立符號鏈接,將這個盤符和在這一屆最開始建立的符號鏈接聯繫起來。

//分配用戶指定大小的非分頁內存,並使用我們自己的內存 TAG 值
pDeviceExtension->DiskImage = ExAllocatePoolWithTag(
	NonPagedPool,
    pDeviceExtension->DiskRegInfo.DiskSize,
    RAMDISK_TAG
);
//下面的代碼只有在內存分配成功時才運行
if(pDeviceExtension->DiskImage) {
    UNICODE_STRING deviceName;
    UNICODE_STRING win32Name;
    //在這裏調用我們自己實現的函數去初始化磁盤
    RamDiskFormatDisk(pDeviceExtension);
    status = STATUS_SUCCESS;
    //初始化一個內容爲 "\\DosDevices\\" 的UNICODE_STRING 的變量
    RtlInitUnicodeString(&win32Name,DOS_DEVICE_NAME);
    //初始化一個內容爲 "\\Devices\\RamDisk" 的UNICODE_STRING 的變量,這裏無用
    //爲了保持原文檔的完整性
    RtlInitUnicodeString(&deviceName,NT_DEVICE_NAME);
    //準備好用來存儲符號鏈接名的 UNICODE_STRING 變量
    pDeviceExtension->SymbolicLink.Buffer = (PWSTR)&pDeviceExtension->DosDeviceNameBuffer;
    pDeviceExtension->SymbolicLink.MaxmemLength = sizeof(&pDeviceExtension->DosDeviceNameBuffer);
    pDeviceExtension->SymbolicLink.Length = win32Name.Length;
    
    //將符號鏈接名的一開始設置爲 "\\DosDevices\\",這是所有符號鏈接共有的前綴
    RtlCopyUnicodeString(&pDeviceExtension->SymbolicLink,&win32Name);
    //後面緊跟我們從用戶配置中讀出來指定的盤符
    RtlAppendUnicodeStringToString(
    	&pDeviceExtension->SymbolicLink,
        &pDeviceExtension->DiskReginfo.DriverLetter
    );
    //現在符號鏈接名已經準備就緒,調用 WDF 驅動框架模型提供的
    //函數 來爲之前生成的設備建立符號鏈接
    status = WdfDeviceCreateSymbolicLink(
    	device,
        &pDeviceExtension->SymbolicLink
    );
 
}
//結束
return status;

9.4.7 小結

​ 至此,完整的分析了 RamDisk 驅動中 EvtDriverDeviceAdd 函數的具體實現。這個函數建立了設備對象,爲了處理各種請求又建立了處理隊列。同時註冊了三個函數處理讀寫和 DeviceControl 請求。然後根據用戶在註冊表中的配置參數初始化了一些相關屬性。爲設備存儲數據準備好了內存,建立了盤符。至此,RamDisk 建立完成。它在內核中代表了這個內存盤。但是我們還不瞭解磁盤卷的結構,所以不清楚如何去處理各種請求。接下來會繼續學習磁盤卷的結構。

9.5 FAT12/16 磁盤卷的初始化

9.5.1 磁盤卷結構簡介

​ 我們學習一個最簡單的磁盤結構,即一個分區的基於 FAT12/16 的磁盤。Windows 磁盤卷首先繼承了它所在磁盤的特性,這些特性是由硬件決定的,不可設置,不可改變。這些特性包括:

  • 每扇區的字節數。扇區是磁盤讀寫的基本單位,由於硬盤的物理設計導致它不能一次讀寫一個字節,而是一次最少讀寫一個扇區,現在的所有硬盤扇區大小爲 512 字節。
  • 每磁道的扇區數。磁盤的盤片是原型的,讀者可以認爲每一個磁道都是一個源泉。整個盤片被劃分成多個同心圓,即多個磁道。每個磁道具有相同的扇區數。
  • 每柱面的磁道數。磁盤是由許多個盤片組成的,成爲一個圓柱體。可以認爲所有盤片同一位置的磁道組成了一個柱面。
  • 柱面數。表示硬盤的一個圓形盤片能夠劃分出多少個同心圓。

​ 在 FAT12/16 文件系統中,有這麼幾個參數需要解釋一下:

  • MBR。主引導記錄,位於磁盤的第一個扇區,大小正好是一個扇區的大小。MBR 的起始處是一段程序,在 BIOS 的代碼執行到最後時,BIOS 會將這段程序加載到內存中並開始執行。在這段程序的後面是一個硬盤分區表,用於記錄當前磁盤具體的分區信息。由於 RamDisk 驅動只是用來建立一個可用的磁盤卷,並不要求這個卷可以引導和具有其他一些特徵,所以在 RamDisk 的磁盤鏡像中並不會看到 MBR 部分的存在。

  • DBR。 DBR 是 DOS Boot Record (操作系統引導記錄) 的簡稱。MBR 的硬盤分區表中會記錄每個分區的信息,包括起始位置等。而 DBR 就存在這個起始位置指向的第一個扇區裏,DBR 裏面包括了有效的引導程序、廠商標誌、描述數據區等。引導程序是一段用來加載真正操作系統的程序,在DBR 的最開始是一個跳轉指令,跳轉到 DBR 後面一點的引導程序處。廠商標誌又叫 OEM 串,一般是由格式化程序寫的。數據描述區又稱爲 BPB 的數據塊,記錄了這個分區的衆多信息,這些信息用於系統在爲這個邏輯盤建立系統時做初始參數,如: 文件系統格式、根目錄大小和簇大小等。作爲文件系統的一個組成部分, DBR 是由操作系統的格式化程序建立的,在文件系統驅動操作任何一個磁盤卷時,這一部分的信息將被讀取作爲文件系統在這個磁盤捲上的參數被使用。所以在 RamDisk 驅動中,DBR 部分是需要存在的。

  • FAT區。FAT 是 File Allocation Table (文件分配表) 的簡稱。位於 DBR 之後,並且以一式兩份的形式連續保存。FAT表 實際上是一個鏈表,它的每個表項的編號都代表磁盤上的一個簇,每個表項的內容都是另一個簇的編號。而一條完整的鏈表就代表了一個文件在磁盤上所佔有的簇。FAT 表的 0 和 1 項是被保留的,從第二項開始用來記錄某個文件所在的位置。由此可知,FAT 表的大小隻和磁盤的大小以及這個磁盤上的文件系統對每個簇的大小定義有關。FAT12 和 16 的區別僅僅是一個 FAT 表的表項中能用幾位做存儲空間。

  • 根目錄入口點。FAT 是多個鏈表的集合體,其中的每一條鏈代表了一個文件。但是這些鏈起始點如何確定呢?多個根目錄入口點形成了一個表,這個表緊跟着 FAT 表存儲,這個表的每個表項代表了根目錄下的一個文件或者一個目錄,這個表項上記載了很多相關的信息,如文件名,修改時間,最重要的是記錄了這個文件在 FAT 表中的起點。這樣就可以通過查詢 FAT 表找到這個文件或者目錄的所有簇,進而獲取所有的數據。而通過查詢每個目錄的目錄項內容又可以知道這個目錄下面存儲的文件在 FAT 表中的位置,這樣就能遍歷到磁盤上所有的文件了。

    ​ 以上知識,作爲磁盤內容的小知識瞭解。有些會用到,有些不會。但是瞭解即可!對於所學的大部分內容,感覺在不用的時候有一個小印象就行,在用的時候知道如何去快速查找並且能迅速撿起來用即可。

明日計劃

  • 論文開寫
  • 繼續學習驅動編程
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章