【Window核心驅動開發】——記憶體對映的基本使用
【我的】Window驅動開發——記憶體對映的基本使用
作者:zcr214 時間:2016/5/18
記憶體對映檔案可以用於3個不同的目的。
•系統使用記憶體對映檔案,以便載入和執行. exe和DLL檔案。這可以大大節省頁檔案空間和應用程式啟動執行所需的時間。
•可以使用記憶體對映檔案來訪問磁碟上的資料檔案。這使你可以不必對檔案執行I/O操作,並且可以不必對檔案內容進行快取。
•可以使用記憶體對映檔案,使同一臺計算機上執行的多個程序能夠相互之間共享資料。Windows確實提供了其他一些方法,以便在程序之間進行資料通訊,但是這些方法都是使用記憶體對映檔案來實現的,這使得記憶體對映檔案成為單個計算機上的多個程序互相進行通訊的最有效的方法。
下面就來使用一下記憶體對映讀取檔案。
1. 建立或開啟檔案物件
首先當然要有檔案物件,先要建立或者開啟檔案,使用ZwCreateFile這個函式是比較方便的,也可以使用ZwOpenFile,但是它只是開啟,而ZwCreateFile函式可以通過設定CreateDisposition引數來達到不同的建立行為,如設為FILE_OPEN_IF是開啟檔案,如果已存在,則新建檔案。下面是這個函式原型。
NTSTATUS
NTAPI
ZwCreateFile(
_Out_PHANDLE FileHandle,
_In_ACCESS_MASK DesiredAccess,
_In_POBJECT_ATTRIBUTES
_Out_PIO_STATUS_BLOCK IoStatusBlock,
_In_opt_PLARGE_INTEGER AllocationSize,
_In_ULONG FileAttributes,
_In_ULONG ShareAccess,
_In_ULONG CreateDisposition,
_In_ULONG CreateOptions,
_In_reads_bytes_opt_(EaLength)PVOID EaBuffer,
_In_ULONG EaLength
);
引數說明:
FileHandle即是檔案控制代碼,建立檔案物件之後會返回檔案控制代碼。
DesireAccess表示希望獲取的許可權,可以有以下四種,從字面意思即可理解。
GENERIC_READ
GENERIC_WRITE
GENERIC_EXECUTE
GENERIC_ALL
ObjectAttributes和以前在其他函式中使用的物件屬性一樣,需要初始化一些屬性值。包括物件名稱,長度,安全描述符等內容。我們這裡如下初始化:
OBJECT_ATTRIBUTESobj_attr={0};
UNICODE_STRINGfilename_Uni=RTL_CONSTANT_STRING(L"\\??\\e:\\filetestzcr\\test.txt");
InitializeObjectAttributes(&obj_attr,
&filename_Uni,
OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE,
NULL,
NULL);
其中的OBJ_KERNEL_HANDLE屬性表示在核心態。更多的屬性可以參考MSDN的說明文件。
IoStatusBlock是返回的這個操作的狀態資訊。包括下面幾種,當然如果建立或開啟成功,則這個返回的引數就沒啥更多的用了。
FILE_CREATED
FILE_OPENED
FILE_OVERWRITTEN
FILE_SUPERSEDED
FILE_EXISTS
FILE_DOES_NOT_EXIST
AllocationSize表示初始指派的大小,可以為NULL,即不在初始的時候指派。如果最終沒有檔案被建立或覆寫,那這個引數就會被忽略。(我們現在是要開啟一個已存在的檔案,所以這個引數不需要使用,設為NULL就可以了)
FileAttributes表示檔案屬性標識,通常都設為FILE_ATTRIBUTE_NORMAL。
ShareAccess即共享許可權,表示其他執行緒可以對這個檔案物件的許可權。主要有讀寫和刪除3種。
FILE_SHARE_READ
FILE_SHARE_WRITE
FILE_SHARE_DELETE
CreateDisposition定義在檔案存在與否的不同情況下,函式如何處理。例如剛才提到的FILE_OPEN_IF,就是指開啟檔案,如果不存在則新建一個檔案。
CreateOptions是建立或開啟檔案的一些選項,可以選1個或多個。
最後兩個引數必須是NULL和0。
函式介紹完畢,於是,我們這裡建立檔案可以寫成:
HANDLEhFile=NULL;
IO_STATUS_BLOCKio_status={0};
status=ZwCreateFile(&hFile,
GENERIC_ALL,
&obj_attr,
&io_status,
NULL,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ,
FILE_OPEN_IF,
FILE_NON_DIRECTORY_FILE|FILE_RANDOM_ACCESS|FILE_SYNCHRONOUS_IO_NONALERT,
NULL,
0
);
如果建立成功,那麼檔案控制代碼儲存在hFile中。
2. 建立對映單元
有了檔案控制代碼,就可以利用它來建立檔案對映。在核心態,用到ZwCreateSection這個函式:
NTSTATUS
NTAPI
ZwCreateSection(
_Out_PHANDLE SectionHandle,
_In_ACCESS_MASK DesiredAccess,
_In_opt_POBJECT_ATTRIBUTES ObjectAttributes,
_In_opt_PLARGE_INTEGER MaximumSize,
_In_ULONG SectionPageProtection,
_In_ULONG AllocationAttributes,
_In_opt_HANDLE FileHandle
);
引數說明:
SectionHandle類似之前的檔案控制代碼,這個表示對映單元控制代碼。建立成功後返回到這裡。
DesireAccess表示想要的許可權,主要有
SECTION_QUERY
SECTION_MAP_WRITE
SECTION_MAP_READ
SECTION_MAP_EXECUTE
SECTION_EXTEND_SIZE
SECTION_ALL_ACCESS
ObjectAttributes和之前一樣是物件屬性,但是這裡要注意,在初始化時必須使用OBJ_KERNEL_HANDLE標識。當然,也可以設為空。
MaximumSize表示按位元組byte計算的最大值。
SectionPageProtection用來設定對映單元的頁保護標識,主要有4種。
PAGE_READONLY
PAGE_READWRITE
PAGE_EXECUTE
PAGE_WRITECOPY
AllocationAttributes用來設定建立對映單元的方案。主要有4種。
SEC_FILE
SEC_RESERVE 用到時才建立
SEC_COMMIT 立刻建立
SEC_LARGE_PAGES
FileHandle即剛才建立的檔案物件控制代碼。
引數說明完畢,接下來我們利用已有的hFile創建出記憶體對映單元。
HANDLEh_section=NULL;
LARGE_INTEGERmaxsize={4096};
status=ZwCreateSection(&h_section,
SECTION_MAP_READ,
NULL,/* &obj_attr,*/
&maxsize,
PAGE_READWRITE,
SEC_COMMIT,
hFile);
如果建立成功,則對映單元的控制代碼儲存在了h_section中。
3. 對映
成功建立了對映單元之後,接下里就可以開始獲取對映的內容了,使用ZwMapViewOfSection函式,其原型如下:
NTSTATUS
NTAPI
ZwMapViewOfSection(
_In_HANDLE SectionHandle,
_In_HANDLE ProcessHandle,
_Outptr_result_bytebuffer_(*ViewSize)PVOID*BaseAddress,
_In_ULONG_PTR ZeroBits,
_In_SIZE_T CommitSize,
_Inout_opt_PLARGE_INTEGER SectionOffset,
_Inout_PSIZE_T ViewSize,
_In_SECTION_INHERIT InheritDisposition,
_In_ULONG AllocationType,
_In_ULONG Win32Protect
);
引數說明:
SectionHandle是上面建立成功的對映單元控制代碼。
ProcessHandle是當前的程序控制代碼,這裡可以填寫NtCurrentProcess(),也可以填寫(HANDLE)-1。一定不可以填錯,否則就會失敗。
BaseAddress即是用來獲取對映到的具體內容的指標,對映成功就會儲存到這裡。
ZeroBits必須設定為0.
CommitSize用來初始化對映範圍的大小,按位元組byte計算,只對頁檔案有效。
SectionOffset偏移量,通常設為0。
ViewSize一個Size_T的指標,表示對映的總大小,設定為0的話,則這個對映行為就會從偏移量最開始的位置一直到檔案結束。所以大部分情況下都是設定為0的。
InheritDisposition設定子程序是否可以共享這個對映單元,ViewShare就是可以共享,ViewUnmap就是不共享。
AllocationType設定一系列的指派型別,通常設定為MEM_RESERVE。
Win32Protect初始化保護標識。主要有以下4種
PAGE_NOACCESS
PAGE_READONLY
PAGE_READWRITE
PAGE_WRITECOPY
引數說明完畢,接下來就可以執行映射了。
PVOIDMapFileBaseAddress=NULL;
SIZE_Tsize=0;
status=ZwMapViewOfSection(h_section,
/*(HANDLE)-1,*/NtCurrentProcess(),
(PVOID*)&MapFileBaseAddress,
0,
1024,
0,
&size,
ViewShare,
MEM_RESERVE,
PAGE_READWRITE);
如果成功,那麼MapFileBaseAddress已經拿到了對映的內容,可以嘗試打印出來看看DbgPrint("storage is:%5s",MapFileBaseAddress);
4. 關閉對映
當然,做完了對映不能忘了釋放它,使用ZwUnmapViewOfSection函式,要注意的是第一個引數是程序控制代碼,不是對映單元控制代碼,不要弄錯了。
status=ZwUnmapViewOfSection(NtCurrentProcess(),MapFileBaseAddress);
還要關閉檔案控制代碼和對映單元控制代碼,直接使用ZwClose函式即可。
ZwClose(hFile);
ZwClose(h_section);
為了更加安全,最好把這些指標設定為空。
MapFileBaseAddress=NULL;
hFile=NULL;
h_section=NULL;