天天看點

7、Windows驅動開發技術詳解筆記(3) 基本文法回顧

3、檔案讀寫

在ring3 我們可以使用CreateFile、ReadFile 、WriteFile 等API,在ring0 同樣很相似,不過函數變成了ZwCreateFile、ZwReadFile、ZwWriteFile 等核心函數。

1)ZwCreateFile與ring3的CreateFile函數有所不同,它不能直接将需要打開或建立的檔案路徑傳遞過去,我們必須首先填寫一個OBJECT_ATTRIBUTES結構。

UNICODE_STRING str;

OBJECT_ATTRIBUTES obj_attrib;

RtlInitUnicodeString(&str, L"\\??\\C:\\windows\\notepad.exe");

InitializeObjectAttributes(&obj_attrib,

&str, // 需要操作的對象、比如檔案或系統資料庫路徑等

OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,

NULL,

NULL);

第三個參數OBJ_CASE_INSENSITIVE 表示不區分大小寫,OBJ_KERNEL_HANDLE

表示将要打開的句柄為核心句柄。核心句柄比起應用層句柄有很多的好處,例如它可以不受程序或線程的限制,而且在需要打開一個核心句柄時不需要考慮目前是否有權限通路該檔案的問題。

2)建立、打開檔案

建立和打開檔案都可使用ZwCreateFile 函數,它的第一個參數将傳回一個檔案句柄,所有後續操作都可以通過這個句柄完成,在操作結束後,需要調用ZwClose 關閉句柄。

<a href="http://msdn.microsoft.com/en-us/library/ff566424%28VS.85%29.aspx">http://msdn.microsoft.com/en-us/library/ff566424%28VS.85%29.aspx</a>

ZwCreateFile 函數的第三個參數就是使用我們此前填寫的OBJECT_ATTRIBUTES 結構;它傳回的資訊通過第四個IO_STATUS_BLOCK 傳回;第八、九個參數聯合指明了如何打開或建立檔案,其中IO_STATUS_BLOCK的定義如下所示:

typedef struct _IO_STATUS_BLOCK {

NTSTATUS Status;

ULONG Information;

}IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;

http://msdn.microsoft.com/en-us/library/ff550671%28VS.85%29.aspx

其中Status指明了函數的執行結果,如果執行成功它的值将是STATUS_SUCCESS ,否

則它将會是一個形如STATUS_XXX 的錯誤提示。

此外,DDK 還提供了一個函數ZwOpenFile 用來簡化打開檔案的操作,它所需要的參數

比ZwCreateFile 更加簡潔,使用更加簡單。

3)打開檔案

在核心中讀寫檔案與使用者模式下十分相似,它們分别使用ZwReadFile 和ZwWriteFile函數完成。

NTSTATUS

ZwReadFile(

IN HANDLE FileHandle,

IN HANDLE Event OPTIONAL,

IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,

IN PVOID ApcContext OPTIONAL,

OUT PIO_STATUS_BLOCK IoStatusBlock,

OUT PVOID Buffer,

IN ULONG Length,

IN PLARGE_INTEGER ByteOffset OPTIONAL,

IN PULONG Key OPTIONAL);

各參數的簡要介紹如下所示:

FileHandle:函數ZwCreateFile 傳回的句柄。如果它是一個核心句柄,則ZwReadFile 和

ZwCreateFile 并不需要在同一個程序中,因為核心句柄是各程序通用的。

Event :一個事件,用于異步完成讀時;我們忽略這個參數。

ApcRoutine Apc:回調例程,用于異步完成讀時;我們忽略這個參數。

IoStatusBlock:傳回結果狀态,與ZwCreateFile 中的同名參數相同。

Buffer:緩沖區,如果讀取檔案的内容成功,則内容将被讀取到這裡。

Length:描述緩沖區的長度,即試圖讀取檔案的長度。

ByteOffset:要讀取的檔案的偏移量,也就是要讀取的内容在檔案中的位置。一般來說,不要将其設定為NULL,檔案句柄不一定支援直接讀取目前偏移。

Key:讀取檔案時用的一種附加資訊,一般不使用。

當函數執行成功時傳回STATUS_SUCCESS,實際上隻要能夠讀取到任意位元組的資料(不管它是否符合參數Length 的要求),都傳回成功;但是,如果僅讀取檔案長度之外的部分,則傳回STATUS_END_OF_FILE。ZwWriteFile 的參數與ZwReadFile 基本相同。

4)其它操作

ZwQueryInformationFile、ZwSetInformationFile可以分别用來擷取和設定檔案屬性,包括檔案大小、檔案指針位置、檔案屬性(如隻讀、隐藏)、檔案建立/修改日期等。

ZwSetInformationFile 函數:

ZwSetInformationFile(

IN PVOID FileInformation,

IN FILE_INFORMATION_CLASS FileInformationClass

);

FileInformationClass指定修改或查詢的類别,它可能的值有很多種

<a href="http://msdn.microsoft.com/en-us/library/ff567096%28VS.85%29.aspx">http://msdn.microsoft.com/en-us/library/ff567096%28VS.85%29.aspx</a>

在核心模式下操作檔案的函數不像使用者模式下那樣豐富,想複制檔案就調用CopyFile、想删除檔案就調用DeleteFile等,在核心模式下除了讀寫檔案的其他所有操作都是通過這兩個ZwQueryInformation和ZwSetInformationFile 函數完成的,而如何使這兩個函數精确完成我們需要的功能,就需要通過FileInformationClass參數來指定。

5)一個例子如下:

聲明.h:

7、Windows驅動開發技術詳解筆記(3) 基本文法回顧
7、Windows驅動開發技術詳解筆記(3) 基本文法回顧

代碼

1 BOOLEAN

2

3 MyCopyFile(IN PUNICODE_STRING ustrDestFile,

4

5 IN PUNICODE_STRING ustrSrcFile);

6

7 .cpp定義

8

9 DriverEntry函數中

10

11 UNICODE_STRING ustrSrcFile, ustrDestFile;

12

13 RtlInitUnicodeString(&amp;ustrSrcFile, L"\\??\\C:\\windows\\notepad.exe");

14

15 RtlInitUnicodeString(&amp;ustrDestFile, L"\\??\\C:\\notepad.exe");

16

17  if(MyCopyFile(&amp;ustrDestFile, &amp;ustrSrcFile))

18

19 {

20

21 KdPrint(("[Test] CopyFile Success!"));

22

23 }

24

25  else

26

27 {

28

29 KdPrint(("[Test] CopyFile Error!"));

30

31 }

32

33 return status;

34

35 }

36

37 自定義函數:

38

39 /************************************************************************

40

41 * 函數名稱:MyCopyFile

42

43 * 功能描述:複制檔案

44

45 * 參數清單:

46

47 ustrDestFile:目的檔案

48

49 ustrSrcFile:源檔案

50

51 * 傳回 值:傳回狀态

52

53 *************************************************************************/

54

55 BOOLEAN

56

57 MyCopyFile( IN PUNICODE_STRING ustrDestFile,

58

59 IN PUNICODE_STRING ustrSrcFile)

60

61 {

62

63 HANDLE hSrcFile, hDestFile;

64

65 PVOID buffer = NULL;

66

67 ULONG length = 0;

68

69 LARGE_INTEGER offset = {0};

70

71 IO_STATUS_BLOCK Io_Status_Block = {0};

72

73 OBJECT_ATTRIBUTES obj_attrib;

74

75 NTSTATUS status;

76

77 BOOLEAN bRet = FALSE;

78

79 do

80

81 { // 打開源檔案

82

83 InitializeObjectAttributes(&amp;obj_attrib,

84

85 ustrSrcFile,

86

87 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,

88

89 NULL,

90

91 NULL);

92

93 status = ZwCreateFile(&amp;hSrcFile,

94

95 GENERIC_READ,

96

97 &amp;obj_attrib,

98

99 &amp;Io_Status_Block,

100

101 NULL,

102

103 FILE_ATTRIBUTE_NORMAL,

104

105 FILE_SHARE_READ,

106

107 FILE_OPEN,

108

109 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,

110

111 NULL, 0);

112

113 if (!NT_SUCCESS(status))

114

115 {

116

117 bRet = FALSE;

118

119 goto END;

120

121 }

122

123 // 打開目标檔案

124

125 InitializeObjectAttributes(&amp;obj_attrib,

126

127 ustrDestFile,

128

129 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,

130

131 NULL,

132

133 NULL);

134

135 status = ZwCreateFile(&amp;hDestFile,

136

137 GENERIC_WRITE,

138

139 &amp;obj_attrib,

140

141 &amp;Io_Status_Block,

142

143 NULL,

144

145 FILE_ATTRIBUTE_NORMAL,

146

147 FILE_SHARE_READ,

148

149 FILE_OPEN_IF,

150

151 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,

152

153 NULL, 0);

154

155 if (!NT_SUCCESS(status))

156

157 {

158

159 bRet = FALSE;

160

161 goto END;

162

163 }

164

165 // 為buffer 配置設定4KB 空間

166

167 buffer = ExAllocatePool(NonPagedPool, 1024 * 4);

168

169 if (buffer == NULL)

170

171 {

172

173 bRet = FALSE;

174

175 goto END;

176

177 }

178

179 // 複制檔案

180

181 while (1)

182

183 {

184

185 length = 4 * 1024;

186

187 // 讀取源檔案

188

189 status = ZwReadFile(hSrcFile,

190

191 NULL,

192

193 NULL,

194

195 NULL,

196

197 &amp;Io_Status_Block,

198

199 buffer,

200

201 length,

202

203 &amp;offset,

204

205 NULL);

206

207 if (!NT_SUCCESS(status))

208

209 {

210

211 // 如果狀态為STATUS_END_OF_FILE,說明檔案已經讀取到末尾

212

213 if (status == STATUS_END_OF_FILE)

214

215 {

216

217 bRet = TRUE;

218

219 goto END;

220

221 }

222

223 }

224

225 // 獲得實際讀取的長度

226

227 length = Io_Status_Block.Information;

228

229 // 寫入到目标檔案

230

231 status = ZwWriteFile(hDestFile,

232

233 NULL,

234

235 NULL,

236

237 NULL,

238

239 &amp;Io_Status_Block,

240

241 buffer,

242

243 length,

244

245 &amp;offset,

246

247 NULL);

248

249 if (!NT_SUCCESS(status))

250

251 {

252

253 bRet = FALSE;

254

255 goto END;

256

257 }

258

259 // 移動檔案指針

260

261 offset.QuadPart += length;

262

263 }

264

265 } while (0);

266

267 END:

268

269 if (hSrcFile)

270

271 {

272

273 ZwClose(hSrcFile);

274

275 }

276

277 if (hDestFile)

278

279 {

280

281 ZwClose(hDestFile);

282

283 }

284

285 if (buffer = NULL)

286

287 {

288

289 ExFreePool(buffer);

290

291 }

292

293 return bRet;

294

295 }

6)建立檔案

NTSTATUS ZwCreateFile(

OUT PHANDLE FileHandle,

IN ACCESS_MASK DesiredAccess,

IN POBJECT_ATTRIBUTES ObjectAttribute,

IN PLARGE_INTEGER AllocationSize OPTIONAL,

IN ULONG FileAttributes,

IN ULONG ShareAccess,

IN ULONG CreateDisposition,

IN ULONG createOptions,

IN PVOID EaBuffer OPTIONAL,

IN ULONG EaLength);

FileHandle:是一個句柄的指針。如果這個函數調用傳回成成功(STATUS_SUCCESS),那就麼打開的檔案句柄就傳回在這個位址内。

DesiredAccess:申請的權限。打開寫檔案用FILE_WRITE_DATA,讀檔案内容用FILE_READ_DATA,删除檔案或者把檔案改名用DELETE,想設定檔案屬性,請使用FILE_WRITE_ATTRIBUTES,讀檔案屬性則使用 FILE_READ_ATTRIBUTES。這些條件可以用|(位或)來組合。有兩個宏分别組合了常用的讀權限和常用的寫權限,為GENERIC_READ和GENERIC_WRITE,宏 GENERIC_ALL代表全部權限。如果想同步的打開檔案,加上SYNCHRONIZE。

union {

PVOID Pointer;

};

ULONG_PTR Information;

} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;

實際程式設計中很少用到Pointer。傳回的結果在Status中。成功則為STATUS_SUCCESS。否則則是一個錯誤碼。進一步的資訊在 Information中。不同的情況下傳回的Information的資訊意義不同。針對ZwCreateFile調用的情況,Information 的傳回值有以下幾種可能:

·FILE_CREATED:檔案被成功的建立了。

·FILE_OPENED: 檔案被打開了。

·FILE_OVERWRITTEN:檔案被覆寫了。

·FILE_SUPERSEDED: 檔案被替代了。

·FILE_EXISTS:檔案已存在。(因而打開失敗了)。

·FILE_DOES_NOT_EXIST:檔案不存在。(因而打開失敗了)。

這些傳回值和打開檔案的意圖有關(有時希望打開已存在的檔案,有時則希望建立新的檔案等。

AllocationSize參數很少使用,請設定為NULL。

FileAttributes控制建立立的檔案的屬性,設為FILE_ATTRIBUTE_NORMAL。

ShareAccess,實際上,這是在本代碼打開這個檔案的時候,允許别的代碼同時打開這個檔案所持有的權限。是以稱為共享通路。一共有三種共享标記可以設定:FILE_SHARE_READ、FILE_SHARE_WRITE、FILE_SHARE_DELETE。這三個标記可以用|(位或)來組合。舉例如下:如果本次打開隻使用了 FILE_SHARE_READ,那麼這個檔案在本次打開之後,關閉之前,别次打開試圖以讀權限打開,則被允許,可以成功打開。如果别次打開試圖以寫權限打開,則一定失敗。傳回共享沖突。

CreateDisposition參數說明了這次打開的意圖。可能的選擇如下(這些選擇不能組合):

·FILE_CREATE:建立檔案。如果檔案已經存在,則這個請求失敗。

·FILE_OPEN:打開檔案。如果檔案不存在,則請求失敗。

·FILE_OPEN_IF:打開或建立。如果檔案存在,則打開。如果不存在,則失敗。

·FILE_OVERWRITE:覆寫。如果檔案存在,則打開并覆寫其内容。如果檔案不存在,這個請求傳回失敗。

·FILE_OVERWRITE_IF:建立或覆寫。如果要打開的檔案已存在,則打開它,并覆寫其記憶體。如果不存在,則簡單的建立新檔案。

·FILE_SUPERSEDE:建立或取代。如果要打開的檔案已存在。則生成一個新檔案替代之。如果不存在,則簡單的生成新檔案。

CreateOption, 筆者使用FILE_NON_DIRECTORY_FILE| FILE_SYNCHRONOUS_IO_NONALERT,此時檔案被同步的打開。而且打開的是檔案(而不是目錄。建立目錄請用FILE_ DIRECTORY_FILE)。同步的打開的意義在于,以後每次操作檔案的時候,比如寫入檔案,調用ZwWriteFile,在ZwWriteFile 傳回時,檔案寫操作已經得到了完成。而不會有傳回STATUS_PENDING(未決)的情況。在非同步檔案的情況下,傳回未決是常見的。此時檔案請求沒有完成,使用者需要等待事件來等待請求的完成。當然,好處是使用者可以先去做别的事情。

要同步打開,前面的DesiredAccess必須含有SYNCHRONIZE。

此外還有一些其他的情況。比如不通過緩沖操作檔案,希望每次讀寫檔案都是直接往磁盤上操作的,此時CreateOptions中應該帶标記 FILE_NO_INTERMEDIATE_BUFFERING。帶了這個标記後,請注意操作檔案每次讀寫都必須以磁盤扇區大小(最常見的是512位元組)對齊,否則會傳回錯誤。

繼續閱讀