提供对文件的读写功能是操作系统的一项重要任务。我们来看一下NT家族的操作系统都为我们提供了那些功能。
9.1 核心句柄表
在开始讨论本文的主题之前,我们先来讨论一个重要的问题,我们之前并未对其给予应有的注意。为了取得对象的句柄需要填充OBJECT_ATTRIBUTES结构体我们已经做过很多遍了,其样子如下:

代码:
InitializeObjectAttributes(oa, @g_usName,OBJ_CASE_INSENSITIVE,0, nil); 
初始化了OBJECT_ATTRIBUTES后,我们调用函数创建或打开这个对象并获得其句柄(handle)。但这个句柄进入的是得到上下文的那个进程的句柄表。因为对于进程句柄表是其特有的,所以使用这个句柄就只能在进程自己的上下文中。例如,若是试图在其它进程的上下文中打开这个句柄的话,好的情况下会操作失败,而运气不好的话,要是在这个进程句柄表中有取值相同的句柄要知道句柄只是一个32位的数(准确地讲是一个位的结构体)就可能关闭其它对象。甚至如果获得的句柄是驱动句柄,但是是在用户进程中,句柄就会进入这个进程的句柄表并有可能有意或无意地在用户模式下使用对象。不希望的事情却总是发生,这样的情况常常出现。正是因此,内核组件和驱动程序有其特殊性,它们不喜欢使用句柄,而是使用reference to object,这样比较好,只需简单地使用指向内存中对象结构体的指针。为了统计对对象的引用,在对象的首部保存着一个引用计数(reference count)。如果需要像本例和上例中的那样访问对象,就要设计一个循环,在不同的上下文中对其进行访问,让系统将句柄放入核心句柄表中(kernel handle table)。
从Windows 2000开始,在系统中有了专门的核心句柄表。在这个表中的句柄只能内核模式下的任意进程上下文中访问,与进程特有的句柄不同。甚至于,比如说如果在System进程的上下文、在DriverEntry函数中获得句柄,则就不能在用户进程上下文中使用对象。System进程实现了自己私有的句柄表,其与核心句柄表不同。
对于在核心句柄表中的句柄,需要在调用InitializeObjectAttributes宏时显式地设置OBJ_KERNEL_HANDLE标志,形式如下:
InitializeObjectAttributes(oa, @g_usName, OBJ_KERNEL_HANDLE, 0,nil); 

9.2 FileWorks驱动程序源代码
就像上一例中的驱动程序,本例的驱动程序的代码也是由几个独立的函数构成的:CreateDirectory、CreateFile、WriteFile、MarkAsReadOnly、ReadFile、UnmarkAsReadOnly、AppendFile、TruncateFile、DeleteFile、DeleteDirectory和EnumerateFiles。几乎所有的函数都是独立工作的。
代码:
unit FileWorks;

interface

uses
  nt_status, ntoskrnl, ntifs, native, winioctl, fcall, macros;

function _DriverEntry(pDriverObject:PDRIVER_OBJECT;
                      pusRegistryPath:PUNICODE_STRING): NTSTATUS; stdcall;

implementation

var
  g_usDirName: UNICODE_STRING;
  g_usFileName: UNICODE_STRING;

procedure CreateDirectory;
var
  oa: OBJECT_ATTRIBUTES;
  iosb: IO_STATUS_BLOCK;
  hDirectory: THANDLE;
  RtnCode: NTSTATUS;
begin
  { 还记得吧, 传递给DbgPrint函数的用于格式化Unicode的代码(%C, %S, %lc, %ls, %wc, %ws, %wZ)只能在
    IRQL = PASSIVE_LEVEL下调用! }
  DbgPrint(#13#10'FileWorks: Creating %ws directory'#13#10, g_usDirName.Buffer);

  InitializeObjectAttributes(oa, @g_usDirName,
                             OBJ_CASE_INSENSITIVE + OBJ_KERNEL_HANDLE,
                             0, nil);
  RtnCode := ZwCreateFile(@hDirectory, SYNCHRONIZE, @oa, @iosb, nil,
                          FILE_ATTRIBUTE_NORMAL, 0, FILE_OPEN_IF,
                          FILE_DIRECTORY_FILE + FILE_SYNCHRONOUS_IO_NONALERT,
                          nil, 0);
  if RtnCode = STATUS_SUCCESS then
  begin
    if iosb.Information = FILE_CREATED then
    begin
      DbgPrint('FileWorks: Directory created'#13#10);
    end else if iosb.Information = FILE_OPENED then
    begin
      DbgPrint('FileWorks: Directory exists and was opened'#13#10);
    end;
    ZwClose(hDirectory);
  end else
  begin
    DbgPrint('FileWorks: Can''t create directory. Status: %08X'#13#10, RtnCode);
  end;
end;

procedure CreateFile;
var
  oa:OBJECT_ATTRIBUTES;
  iosb:IO_STATUS_BLOCK;
  hFile:THANDLE;
  RtnCode: NTSTATUS;
begin
  DbgPrint(#13#10'FileWorks: Creating %ws file'#13#10, g_usFileName.Buffer);
  InitializeObjectAttributes(oa, @g_usFileName,
                             OBJ_CASE_INSENSITIVE + OBJ_KERNEL_HANDLE,
                             0, nil);

  RtnCode := ZwCreateFile(@hFile, SYNCHRONIZE, @oa, @iosb, nil,
                          FILE_ATTRIBUTE_NORMAL, 0,
                          FILE_CREATE,
                          FILE_SYNCHRONOUS_IO_NONALERT, nil, 0);
  if RtnCode = STATUS_SUCCESS then
  begin
    DbgPrint('FileWorks: File created'#13#10);
    ZwClose(hFile);
  end else
  begin
    DbgPrint('FileWorks: Can''t create file. Status: %08X'#13#10, RtnCode);
  end;
end;

procedure WriteFile;
var
  oa:OBJECT_ATTRIBUTES;
  iosb:IO_STATUS_BLOCK;
  hFile:THANDLE;
  RtnCode: NTSTATUS;
  g_szData: PAnsiChar;
begin
  DbgPrint(#13#10'FileWorks: Opening file for writing'#13#10);

  InitializeObjectAttributes(oa, @g_usFileName,
                             OBJ_CASE_INSENSITIVE + OBJ_KERNEL_HANDLE,
                             0, nil);
  RtnCode := ZwCreateFile(@hFile, FILE_WRITE_DATA + SYNCHRONIZE,
                          @oa, @iosb, nil, 0, FILE_SHARE_READ,
                          FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT,
                          nil, 0);
  if RtnCode = STATUS_SUCCESS then
  begin
    DbgPrint('FileWorks: File openeded'#13#10);
    g_szData := 'Data can be written to an open file';
    //RtlInitUnicodeString(g_szData, 'Data can be written to an open file');
    RtnCode := ZwWriteFile(hFile, 0, nil, nil, @iosb, g_szData,
                           strlen(g_szData), nil, nil);
    if RtnCode = STATUS_SUCCESS then
    begin
      DbgPrint('FileWorks: File was written'#13#10);
    end else
    begin
      DbgPrint('FileWorks: Can''t write to the file. Status: %08X\n', RtnCode);
    end;
    ZwClose(hFile);
  end else
  begin
    DbgPrint('FileWorks: Can''t open file. Status: %08X'#13#10, RtnCode);
  end;
end;

procedure MarkAsReadOnly;
var
  oa:OBJECT_ATTRIBUTES;
  iosb:IO_STATUS_BLOCK;
  hFile:THandle;
  fbi:FILE_BASIC_INFORMATION;
  RtnCode:NTSTATUS;
begin
  DbgPrint(#13#10'FileWorks: Opening file for changing attributes'#13#10);
  InitializeObjectAttributes(oa, @g_usFileName,
                             OBJ_CASE_INSENSITIVE + OBJ_KERNEL_HANDLE,
                             0, nil);
  RtnCode := ZwCreateFile(@hFile, FILE_READ_ATTRIBUTES + FILE_WRITE_ATTRIBUTES + SYNCHRONIZE,
                          @oa, @iosb, nil, 0, FILE_SHARE_READ,
                          FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT,
                          nil, 0);
  if RtnCode = STATUS_SUCCESS then
  begin
    DbgPrint('FileWorks: File openeded'#13#10);
    RtnCode := ZwQueryInformationFile(hFile, @iosb, @fbi,
                                      sizeof(fbi),
                                      FileBasicInformation);
    if RtnCode = STATUS_SUCCESS then
    begin
      DbgPrint('FileWorks: File attributes were: %08X'#13#10, fbi.FileAttributes);
      fbi.FileAttributes := fbi.FileAttributes or FILE_ATTRIBUTE_READONLY;
      RtnCode := ZwSetInformationFile(hFile, @iosb, @fbi,
                                      sizeof(fbi), FileBasicInformation);
      if RtnCode = STATUS_SUCCESS then
      begin
        DbgPrint('FileWorks: Now file marked as read-only'#13#10);
      end else
      begin
        DbgPrint('FileWorks: Can''t change file attributes. Status: %08X'#13#10, RtnCode);
      end;
    end else
    begin
      DbgPrint('FileWorks: Can''t query file attributes. Status: %08X'#13#10, RtnCode);
    end;
    ZwClose(hFile);
  end else
  begin
    DbgPrint('FileWorks: Can''t open file. Status: %08X\n', RtnCode);
  end;
end;

procedure ReadFile;
var
  oa:OBJECT_ATTRIBUTES;
  iosb:IO_STATUS_BLOCK;
  hFile:THANDLE;
  p:PVOID;
  cb:DWORD;
  fsi:FILE_STANDARD_INFORMATION;
  RtnCode: NTSTATUS;
begin
  DbgPrint(#13#10'FileWorks: Opening file for reading'#13#10);
  InitializeObjectAttributes(oa, @g_usFileName,
                             OBJ_CASE_INSENSITIVE + OBJ_KERNEL_HANDLE,
                             0, nil);
  RtnCode := ZwOpenFile(@hFile, FILE_READ_DATA + SYNCHRONIZE,
                        @oa, @iosb, FILE_SHARE_READ + FILE_SHARE_WRITE + FILE_SHARE_DELETE,
                        FILE_SYNCHRONOUS_IO_NONALERT);
  if RtnCode = STATUS_SUCCESS then
  begin
    DbgPrint('FileWorks: File openeded'#13#10);
    RtnCode := ZwQueryInformationFile(hFile, @iosb, @fsi,
                                      sizeof(fsi),
                                      FileStandardInformation);
    if RtnCode = STATUS_SUCCESS then
    begin
      cb := fsi.EndOfFile.LowPart + 1;
      p := ExAllocatePool(PagedPool, cb);
      if p <> nil then
      begin
        memset(p, 0, cb);
        RtnCode := ZwReadFile(hFile, 0, nil, nil, @iosb, p, cb, nil, nil);
        if RtnCode = STATUS_SUCCESS then
        begin
          DbgPrint('FileWorks: File content: \=%s\='#13#10, p);
        end else
        begin
          DbgPrint('FileWorks: Can''t read from the file. Status: %08X'#13#10, RtnCode);
        end;
        ExFreePool(p);
      end else
      begin
        DbgPrint('FileWorks: Can''t allocate memory. Status: %08X'#13#10, RtnCode);
      end;
    end else
    begin
      DbgPrint('FileWorks: Can''t query file size. Status: %08X'#13#10, RtnCode);
    end;
    ZwClose(hFile);
  end else
  begin
    DbgPrint('FileWorks: Can''t open file. Status: %08X'#13#10, RtnCode);
  end;
end;

procedure UnmarkAsReadOnly;
var
  oa:OBJECT_ATTRIBUTES;
  iosb:IO_STATUS_BLOCK;
  hFile:THANDLE;
  fbi:FILE_BASIC_INFORMATION;
  RtnCode:NTSTATUS;
begin
  DbgPrint(#13#10'FileWorks: Opening file for changing attributes'#13#10);
  InitializeObjectAttributes(oa, @g_usFileName,
                             OBJ_CASE_INSENSITIVE + OBJ_KERNEL_HANDLE,
                             0, nil);
  RtnCode := ZwCreateFile(@hFile, FILE_READ_ATTRIBUTES + FILE_WRITE_ATTRIBUTES + SYNCHRONIZE,
                          @oa, @iosb, nil, 0, FILE_SHARE_READ,
                          FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT,
                          nil, 0);
  if RtnCode = STATUS_SUCCESS then
  begin
    DbgPrint('FileWorks: File openeded'#13#10);
    RtnCode := ZwQueryInformationFile(hFile, @iosb, @fbi,
                                      sizeof(fbi), FileBasicInformation);
    if RtnCode = STATUS_SUCCESS then
    begin
      DbgPrint('FileWorks: File attributes were: %08X'#13#10, fbi.FileAttributes);
      fbi.FileAttributes := fbi.FileAttributes and (not FILE_ATTRIBUTE_READONLY);
      RtnCode := ZwSetInformationFile(hFile, @iosb, @fbi, sizeof(fbi), FileBasicInformation);
      if RtnCode = STATUS_SUCCESS then
      begin
        DbgPrint('FileWorks: Now file can be written or deleted'#13#10);
      end else
      begin
        DbgPrint('FileWorks: Can''t change file attributes. Status: %08X'#13#10, RtnCode);
      end;
    end else
    begin
      DbgPrint('FileWorks: Can''t query file attributes. Status: %08X'#13#10, RtnCode);
    end;
    ZwClose(hFile);
  end else
  begin
    DbgPrint('FileWorks: Can''t open file. Status: %08X'#13#10, RtnCode);
  end;
end;

procedure AppendFile;
var
  oa:OBJECT_ATTRIBUTES;
  iosb:IO_STATUS_BLOCK;
  hFile:THANDLE;
  g_szDataToAppend:PAnsiChar;
  RtnCode:NTSTATUS;
begin
  DbgPrint(#13#10'FileWorks: Opening file to append data'#13#10);
  InitializeObjectAttributes(oa, @g_usFileName,
                             OBJ_CASE_INSENSITIVE + OBJ_KERNEL_HANDLE,
                             0, nil);
  RtnCode := ZwOpenFile(@hFile, FILE_APPEND_DATA + SYNCHRONIZE,
                        @oa, @iosb, FILE_SHARE_READ,
                        FILE_SYNCHRONOUS_IO_NONALERT);
  if RtnCode = STATUS_SUCCESS then
  begin
    DbgPrint('FileWorks: File openeded'#13#10);
    g_szDataToAppend := ' using ZwWriteFile';
    RtnCode := ZwWriteFile(hFile, 0, nil, nil, @iosb, g_szDataToAppend,
                           strlen(g_szDataToAppend), nil, nil);
    if RtnCode = STATUS_SUCCESS then
    begin
      DbgPrint('FileWorks: Data appended to the file'#13#10);
    end else
    begin
      DbgPrint('FileWorks: Can''t append data to file. Status: %08X'#13#10, RtnCode);
    end;
    ZwClose(hFile);
  end else
  begin
    DbgPrint('FileWorks: Can''t open file. Status: %08X'#13#10, RtnCode);
  end;
end;

procedure TruncateFile;
var
  oa:OBJECT_ATTRIBUTES;
  iosb:IO_STATUS_BLOCK;
  hFile:THANDLE;
  fsi:FILE_STANDARD_INFORMATION;
  feofi:FILE_END_OF_FILE_INFORMATION;
  RtnCode:NTSTATUS;
begin
  DbgPrint(#13#10'FileWorks: Opening file to truncate'#13#10);
  InitializeObjectAttributes(oa, @g_usFileName,
                             OBJ_CASE_INSENSITIVE + OBJ_KERNEL_HANDLE,
                             0, nil);
  RtnCode := ZwOpenFile(@hFile, FILE_WRITE_DATA + SYNCHRONIZE,
                        @oa, @iosb, FILE_SHARE_READ,
                        FILE_SYNCHRONOUS_IO_NONALERT);
  if RtnCode = STATUS_SUCCESS then
  begin
    DbgPrint('FileWorks: File openeded'#13#10);
    RtnCode := ZwQueryInformationFile(hFile, @iosb, @fsi,
                                      sizeof(fsi),
                                      FileStandardInformation);
    if RtnCode = STATUS_SUCCESS then
    begin
      DbgPrint('FileWorks: EOF was: %08X'#13#10, fsi.EndOfFile.LowPart);
      feofi.EndOfFile.HighPart := 0;
      feofi.EndOfFile.LowPart := (fsi.EndOfFile.LowPart) shr 1;
      RtnCode := ZwSetInformationFile(hFile, @iosb, @feofi,
                                      sizeof(feofi),
                                      FileEndOfFileInformation);
      if RtnCode = STATUS_SUCCESS then
      begin
        DbgPrint('FileWorks: File truncated to its half size'#13#10);
      end else
      begin
        DbgPrint('FileWorks: Can''t truncate file. Status: %08X'#13#10, RtnCode);
      end;
    end else
    begin
      DbgPrint('FileWorks: Can''t query file info. Status: %08X'#13#10, RtnCode);
    end;
    ZwClose(hFile);
  end else
  begin
    DbgPrint('FileWorks: Can''t open file. Status: %08X'#13#10, RtnCode);
  end;
end;

procedure DeleteFile;
var
  oa:OBJECT_ATTRIBUTES;
  iosb:IO_STATUS_BLOCK;
  hFile:THANDLE;
  fdi:FILE_DISPOSITION_INFORMATION;
  RtnCode:NTSTATUS;
begin
  DbgPrint(#13#10'FileWorks: Opening file for deletion'#13#10);
  InitializeObjectAttributes(oa, @g_usFileName,
                             OBJ_CASE_INSENSITIVE + OBJ_KERNEL_HANDLE,
                             0, nil);
  RtnCode := ZwCreateFile(@hFile, _DELETE + SYNCHRONIZE,
                          @oa, @iosb, nil, 0, FILE_SHARE_DELETE,
                          FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT,
                          nil, 0);
  if RtnCode = STATUS_SUCCESS then
  begin
    DbgPrint('FileWorks: File openeded'#13#10);
    fdi.DeleteFile := True;
    RtnCode := ZwSetInformationFile(hFile, @iosb, @fdi,
                                    sizeof(fdi),
                                    FileDispositionInformation);
    if RtnCode = STATUS_SUCCESS then
    begin
      DbgPrint('FileWorks: File has been marked for deletion'#13#10);
      DbgPrint('FileWorks: It should be deleted when the last open handle is closed'#13#10);
    end else
    begin
      DbgPrint('FileWorks: Can''t mark file for deletion. Status: %08X'#13#10, RtnCode);
    end;
    ZwClose(hFile);
  end else
  begin
    DbgPrint('FileWorks: Can''t open file. Status: %08X'#13#10, RtnCode);
  end;
end;

procedure DeleteDirectory;
var
  oa:OBJECT_ATTRIBUTES;
  RtnCode:NTSTATUS;
begin
  InitializeObjectAttributes(oa, @g_usDirName,
                             OBJ_CASE_INSENSITIVE + OBJ_KERNEL_HANDLE,
                             0, nil);
  RtnCode := ZwDeleteFile(@oa);
  if RtnCode = STATUS_SUCCESS then
  begin
    DbgPrint(#13#10'FileWorks: Directory should be deleted'#13#10);
  end else
  begin
    DbgPrint(#13#10'FileWorks: Can''t delete directory. Status: %08X'#13#10, RtnCode);
  end;
end;

procedure EnumerateFiles;
var
  oa:OBJECT_ATTRIBUTES;
  hSystemRootDirectory:THANDLE;
  hDriversDirectory:THANDLE;
  _as:ANSI_STRING;
  us:UNICODE_STRING;
  iosb:IO_STATUS_BLOCK;
  tf:TIME_FIELDS;
  cb:DWORD;
  pfdi:PFILE_DIRECTORY_INFORMATION;
  RtnCode:NTSTATUS;
  g_usTemp:UNICODE_STRING;
begin
  DbgPrint(#13#10'FileWorks: Opening directory to enumerate files'#13#10);
  RtlInitUnicodeString(g_usTemp, '\SystemRoot');
  InitializeObjectAttributes(oa, @g_usTemp,
                             OBJ_CASE_INSENSITIVE + OBJ_KERNEL_HANDLE,
                             0, nil);
  RtnCode := ZwOpenFile(@hSystemRootDirectory, FILE_LIST_DIRECTORY + SYNCHRONIZE,
                        @oa, @iosb, FILE_SHARE_READ + FILE_SHARE_WRITE + FILE_SHARE_DELETE,
                        FILE_DIRECTORY_FILE + FILE_SYNCHRONOUS_IO_NONALERT);
  if RtnCode = STATUS_SUCCESS then
  begin
    RtlInitUnicodeString(g_usTemp, 'system32\drivers');
    InitializeObjectAttributes(oa, @g_usTemp,
                               OBJ_CASE_INSENSITIVE + OBJ_KERNEL_HANDLE,
                               hSystemRootDirectory, nil);
    RtnCode := ZwOpenFile(@hDriversDirectory, FILE_LIST_DIRECTORY + SYNCHRONIZE,
                          @oa, @iosb, FILE_SHARE_READ + FILE_SHARE_WRITE + FILE_SHARE_DELETE,
                          FILE_DIRECTORY_FILE + FILE_SYNCHRONOUS_IO_NONALERT);
    if RtnCode = STATUS_SUCCESS then
    begin
      cb := sizeof(FILE_DIRECTORY_INFORMATION) + 256;
      pfdi := ExAllocatePool(PagedPool, cb);
      if pfdi <> nil then
      begin
        RtlInitUnicodeString(g_usTemp, 'c*');
        DbgPrint(#13#10'FileWorks: ---------- Starting enumerate files ----------'#13#10);
        RtnCode := ZwQueryDirectoryFile(hDriversDirectory, 0, nil, nil, @iosb,
                                        pfdi, cb, FileDirectoryInformation,
                                        true, @g_usTemp, true);
        while RtnCode <> STATUS_NO_MORE_FILES do
        begin
          if RtnCode = STATUS_SUCCESS then
          begin
            us.Length := pfdi^.FileNameLength;
            us.MaximumLength := pfdi^.FileNameLength;
            us.Buffer := PWideChar(@pfdi^.FileName);
            if RtlUnicodeStringToAnsiString(@_as, @us, true) = STATUS_SUCCESS then
            begin
              RtlTimeToTimeFields(@pfdi^.CreationTime, @tf);
              DbgPrint(' %s size=%d created on %d.%02d.%04d'#13#10,
                       _as.Buffer, pfdi^.EndOfFile.LowPart,
                       BYTE(tf.Day), BYTE(tf.Month), WORD(tf.Year));
              RtlFreeAnsiString(@_as);
            end;
          end;
          RtnCode := ZwQueryDirectoryFile(hDriversDirectory, 0, nil, nil, @iosb,
                                          pfdi, cb, FileDirectoryInformation,
                                          true, nil, false);
        end; {End While}
        DbgPrint('FileWorks: ------------------------------------------------'#13#10);
        ExFreePool(pfdi);
      end;
      ZwClose(hDriversDirectory);
    end else
    begin
      DbgPrint('FileWorks: Can''t open drivers directory. Status: %08X', RtnCode);
    end;
    ZwClose(hSystemRootDirectory);
  end else
  begin
    DbgPrint('FileWorks: Can''t open system root directory. Status: %08X'#13#10, RtnCode);
  end;
end;

function _DriverEntry(pDriverObject:PDRIVER_OBJECT;
                      pusRegistryPath:PUNICODE_STRING): NTSTATUS; stdcall;
begin
  DbgPrint(#13#10'FileWorks: Entering DriverEntry'#13#10);
  RtlInitUnicodeString(g_usFileName, '\??\c:\FileWorks\test.txt');
  RtlInitUnicodeString(g_usDirName, '\??\c:\FileWorks');
  CreateDirectory;
  CreateFile;
  WriteFile;
  MarkAsReadOnly;
  ReadFile;
  UnmarkAsReadOnly;
  AppendFile;
  ReadFile;
  TruncateFile;
  ReadFile;
  DeleteFile;
  DeleteDirectory;
  EnumerateFiles;
  DbgPrint(#13#10'FileWorks: Leaving DriverEntry'#13#10);
  result := STATUS_DEVICE_CONFIGURATION_ERROR;
end;

end. 
9.3 创建目录与文件
代码:
InitializeObjectAttributes(oa, @g_usDirName, OBJ_CASE_INSENSITIVE + OBJ_KERNEL_HANDLE, 0, nil);
填充OBJECT_ATTRIBUTES结构体,不要忘了OBJ_KERNEL_HANDLE标志。我想要强调的是,在本例中这个操作并不是必需的,因为我们不会在其它进程中使用这些句柄。但是因为FileWorks驱动程序的函数可以很容易的移植到使用任意进程上下文的程序中,所以我决定设置这个标志。如果在您的程序中有驱动和用户进程的共用的句柄,则不应设置此标志。如果只在自己一个进程上下文中使用对象,也不必设置OBJ_KERNEL_HANDLE标志。
创建目录和文件用的都是ZwCreateFile函数。从系统角度看,目录也是文件,所以创建目录的函数与创建文件的函数没有本质上的差别。所以,创建目录与创建文件使用同一个函数,只是创建目录要用FILE_DIRECTORY_FILE标志。
代码:
RtnCode := ZwCreateFile(@hDirectory, SYNCHRONIZE, @oa, @iosb, nil,FILE_ATTRIBUTE_NORMAL, 0, 
                                    FILE_OPEN_IF,FILE_DIRECTORY_FILE+FILE_SYNCHRONOUS_IO_NONALERT,
                                    nil, 0);
 

ZwCreateFile函数有相当多的参数,所以我在下面给出了它的原型,而且不得不使用了C语言的语义,以显示出输入(IN)、输出(OUT)和可选的参数。
代码:
NTSTATUS 
ZwCreateFile(
OUT PHANDLE FileHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes,
OUT PIO_STATUS_BLOCK IoStatusBlock,
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取得所建立的目录的句柄。DesiredAccess定义了对目录的三种访问请求。我们用标志SYNCHRONIZE来定义,这个值的含义在后面会清楚。ObjectAttributes想必您已经知道了。函数成功完成后,参数IoStatusBlock就是指向IO_STATUS_BLOCK结构体的指针,从中可以取出相关的的信息。参数FileAttributes定义了所创建目录的属性(只读、隐藏等)。我们使用FILE_ATTRIBUTE_NORMAL,本例中不需要为目录指定什么特别的属性。可选参数AllocationSize为0定义了所创建文件的大小为0。对于目录来说这是很自然的。参数ShareAccess定义为什么样的值,就要以什么样的权限访问目录。在本例中,我们不允许其它程序访问目录,故将此参数设为0。参数CreateDisposition定义了该目录已经存在,或者相反,文件不存在时系统的行为。我们使用标志FILE_OPEN_IF,这个标志表示如果目录已经存在,就将其打开。我们向参数CreateOptions传递的是标志FILE_DIRECTORY_FILE和FILE_SYNCHRONOUS_IO_NONALERT的组合。第一个表示要建立的是目录而非文件,无需特别解释。FILE_SYNCHRONOUS_IO_NONALERT定义了()对文件所有的操作都要是同步的,例如,调用ZwReadFile后在没有实际读取完文件数据时函数不会返回。在I/O管理器中为文件维护了一个当前文件位置上下文(file position context)。如果设置了标志FILE_SYNCHRONOUS_IO_NONALERT,则在参数DesiredAccess里应该定义为标志SYNCHRONIZE。最后两个参数不用于驱动程序。
代码:
if RtnCode = STATUS_SUCCESS then
begin
    if iosb.Information = FILE_CREATED then
    begin
      ……      
    end else if iosb.Information = FILE_OPENED then
    begin
      ……      
end; 
正如我所讲的,在IO_STATUS_BLOCK结构体中会有额外的信息。
还记得我们是如何处理I/O请求的(见前面的章节)。例如,在驱动程序SharingMemory中对IRP_MJ_CREATE和IRP_MJ_CLOSE的处理如下:
代码:
p_Irp^.IoStatus.Status := STATUS_SUCCESS;
p_Irp^.IoStatus.Information := 0;
IofCompleteRequest(p_Irp, IO_NO_INCREMENT); 
本例的驱动程序也大致相同,即将完成创建文件的请求。只有放入IO_STATUS_BLOCK结构体的域中的值会依赖于请求的类型。
因为我们定义了FILE_OPEN_IF标志,通过iosb.Information的值,我们可以知道是该建立新的目录还是因该目录已经存在而将其打开。
ZwClose(hDirectory); 

我再重复一遍,在内核模式下一定要显式地关闭所有打开的句柄。
代码:
RtnCode := ZwCreateFile(@hFile, SYNCHRONIZE, @oa, @iosb, nil,
                     FILE_ATTRIBUTE_NORMAL, 0,
                     FILE_CREATE,
                     FILE_SYNCHRONOUS_IO_NONALERT, nil, 0); 
如您所见,文件的创建实际上是相同的,只是要去掉FILE_DIRECTORY_FILE标志。而FILE_CREATE标志我将其用于多种情况。它表示只可以创建文件。如果文件已经存在,则对ZwCreateFile会以失败结束。

9.4 文件对象
每一个打开的文件句柄都对应着一个文件对象(file object),在内核内存区中有FILE_OBJECT结构体。
代码:
TFileObject=packed record
    wType:Word;
    Size:Word;
    DeviceObject:PDeviceObject;
    DoNotUser1:Pointer;
    FsContext:Pointer;
    FsContext2:Pointer;
    SectionObjectPointer:Pointer;
    PrivateCacheMap:Pointer;
    FinalStatus:NTSTATUS;
    RelatedFileObject:PFileObject;
    LockOperation:Boolean;
    DeletePending:Boolean;
    ReadAccess:Boolean;
    WriteAccess:Boolean;
    DeleteAccess:Boolean;
    SharedRead:Boolean;
    SharedWrite:Boolean;
    SharedDelete:Boolean;
    Flags:Cardinal;
    FileName:TUnicodeString;
    CurrentByteOffset:TLargeInteger;
    Waiters:Cardinal;
    Busy:Cardinal;
    LastLock:Pointer;
    Lock:TKEvent;
    Event:TKEvent;
   CompletionContext:Pointer;
end; 
例如,我们可以两次打开同一个文件,但是两次的访问请求却不相同:第一次读(FILE_READ_DATA)而第二次写(FILE_WRITE_DATA)。结果内核会建立两个FILE_OBJECT结构体,每一个都对应于自己相应的文件句柄。但是两个句柄和相对应的两个FILE_OBJECT结构体都对应着同一个磁盘文件。第一个FILE_OBJECT结构体将设置ReadAccess域,第二个则设置WriteAccess域。例如,试图写入第一个句柄指向的文件时,系统会发现域WriteAccess = FALSE并结束请求。
DeviceObject域将包含指向设备对象\Device\HarddiskVolume1的指针,因为这个设备是对\??\c:符号链接,我们创建文件时在文件名中使用了\??\c:。
在读完本文后您可能会进行文件操作的实验并会发现系统是如何填充并管理FILE_OBJECT结构体的。如果在内存中定位它时发生问题,可以使用ObReferenceObjectByHandle函数,形式大致如下:
代码:
var
pFileObject:PFILE_OBJECT;
. . .
begin
RtnCode := ObReferenceObjectByHandle(hFile, FILE_READ_DATA, nil, KernelMode, @pFileObject,nil);
  if RtnCode = STATUS_SUCCESS then
begin
    {pFileObject指向对应于hFile的FILE_OBJECT}
    ObfDereferenceObject(pFileObject);
end;
end;
 

ObReferenceObjectByHandle向参数pFileObject中返回指向对应于该文件句柄的FILE_OBJECT结构体的指针。这时,引用计数会增一。之后一定要调用ObfDereferenceObject来使其复原。
9.5 写入文件
到这里我们已经有了大小为0目录和文件。该向文件中写点东西了。
代码:
RtnCode := ZwCreateFile(@hFile, FILE_WRITE_DATA + SYNCHRONIZE,
                     @oa, @iosb, nil, 0, FILE_SHARE_READ,
                     FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT,
                     nil, 0); 
借助于ZwCreateFile不止可以创建文件,还可以打开已有的文件(恰好,不只是文件,其它某些对象也是这样)。为此需要指定标志FILE_OPEN。
为了写入文件,需要相应的访问权使用FILE_WRITE_DATA。
在单元文件中定义了几个常量以用于对文件的一般访问。例如,FILE_GENERIC_WRITE不仅允许向文件中写入数据,还可以更改其属性并添加数据。
代码:
FILE_GENERIC_WRITE = (STANDARD_RIGHTS_WRITE or
                                    FILE_WRITE_DATA or
                                    FILE_WRITE_ATTRIBUTES or
                                    FILE_WRITE_EA or
                                    FILE_APPEND_DATA or
                                    SYNCHRONIZE); 
显然,当我们写文件,其它的程序不应向该文件写入。使用FILE_SHARE_READ标志,则没有程序能写入或是删除文件,而只能读取文件。
代码:
if RtnCode = STATUS_SUCCESS then
begin
    DbgPrint('FileWorks: File openeded'#13#10);
    g_szData := 'Data can be written to an open file';
    RtnCode := ZwWriteFile(hFile, 0, nil, nil, @iosb, g_szData,
                           strlen(g_szData), nil, nil);
 

函数ZwWriteFile见名则知意。
代码:
NTSTATUS 
ZwWriteFile(
IN HANDLE FileHandle,
IN HANDLE Event OPTIONAL,
IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,
IN PVOID ApcContext OPTIONAL,
OUT PIO_STATUS_BLOCK IoStatusBlock,
IN PVOID Buffer,
IN ULONG Length,
IN PLARGE_INTEGER ByteOffset OPTIONAL,
IN PULONG Key OPTIONAL
); 
ZwWriteFile的参数至少要有文件句柄、IO_STATUS_BLOCK结构体指针在该结构体中将放置额外的信息(其中包括向文件写入的字节数)、指向需要写入文件的数据的指针和其大小。
9.6 修改文件属性
我们需要防止我们的文件被删除。
代码:
RtnCode := ZwCreateFile(@hFile, FILE_READ_ATTRIBUTES + FILE_WRITE_ATTRIBUTES +  SNCHRONIZE, @oa, @iosb, nil, 0, FILE_SHARE_READ,
                          FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT,
                          nil, 0); 
标志FILE_READ_ATTRIBUTES和FILE_WRITE_ATTRIBUTES用于取得和更改相应的文件属性。
代码:
if RtnCode = STATUS_SUCCESS then
begin
  DbgPrint('FileWorks: File openeded'#13#10);
  RtnCode := ZwQueryInformationFile(hFile, @iosb, @fbi,
                                sizeof(fbi),
                                FileBasicInformation);
  if RtnCode = STATUS_SUCCESS then
  begin
 

我们需要建立只读属性,但由于文件有其它的属性需要保留,故有:
代码:
fbi.FileAttributes := fbi.FileAttributes or FILE_ATTRIBUTE_READONLY;
RtnCode := ZwSetInformationFile(hFile, @iosb, @fbi,
                            sizeof(fbi), FileBasicInformation); 
要向文件属性中添加属性,我们必须要用标志。当需要根改文件时,我们用UnmarkAsReadOnly函数去掉这个属性,有:
代码:
fbi.FileAttributes := fbi.FileAttributes and (not FILE_ATTRIBUTE_READONLY); 
9.7 读取文件
现在,为了各种操作,我们用ZwOpenFile函数打开文件来从中读取。所需要的参数与ZwCreateFile的类似。
代码:
NTSTATUS
ZwOpenFile(
OUT PHANDLE FileHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes,
OUT PIO_STATUS_BLOCK IoStatusBlock,
IN ULONG ShareAccess,
IN ULONG OpenOptions
); 
代码:
RtnCode := ZwOpenFile(@hFile, FILE_READ_DATA + SYNCHRONIZE,
                        @oa, @iosb, FILE_SHARE_READ + FILE_SHARE_WRITE + FILE_SHARE_DELETE,
                        FILE_SYNCHRONOUS_IO_NONALERT); 

FILE_READ_DATA在这里我们只从文件中读取数据。标志FILE_SHARE_READ、FILE_SHARE_WRITE和FILE_SHARE_DELETE的组合允许对文件进行完全的访问。
if RtnCode = STATUS_SUCCESS then
begin
  DbgPrint('FileWorks: File openeded'#13#10);
  RtnCode := ZwQueryInformationFile(hFile, @iosb, @fsi,
                                sizeof(fsi),
                                FileStandardInformation);
 if RtnCode = STATUS_SUCCESS then
  begin 
我们以准备好读取文件的所有数据,但是用于这个请求的缓冲区的大小依赖于文件的大小。文件的大小可以用信息类FileStandardInformation由函数ZwQueryInformationFile获得,并将其传递给FILE_BASIC_INFORMATION指针。
cb := fsi.EndOfFile.LowPart + 1; 

我们来为最后的零添加一个字节。
   
代码:
 p := ExAllocatePool(PagedPool, cb);
      if p <> nil then
      begin
        memset(p, 0, cb);
        RtnCode := ZwReadFile(hFile, 0, nil, nil, @iosb, p, cb, nil, nil);
        if RtnCode = STATUS_SUCCESS then
        begin
          DbgPrint('FileWorks: File content: \=%s\='#13#10, p);
        End;
        ExFreePool(p);
     end;
 

分配所需的缓冲区,将其清零并传递给ZwReadFile函数。这个函数的原型与ZwWriteFile函数的原型类似,只是缓冲区用处不同。因为在使用之前我们清零了缓冲区,并且其大小比文件的内容多一个字节,所以不会发生将其内容输出到调试信息的问题。
9.8 向文件追加数据
向文件中追加数据有几种方法。可以用标志FILE_WRITE_DATA将其打开,在当前位置建立指向文件末尾的指针,在函数ZwWriteFile的参数ByteOffset中传递偏移量并写入数据。指示文件当前位置的指针保存在FILE_OBJECT.CurrentByteOffset中。我们还可以用标志FILE_APPEND_DATA打开文件,文件当前位置指针会自动指向其末尾。
代码:
RtnCode := ZwOpenFile(@hFile, FILE_APPEND_DATA + SYNCHRONIZE,
                    @oa, @iosb, FILE_SHARE_READ,
                    FILE_SYNCHRONOUS_IO_NONALERT);
if RtnCode = STATUS_SUCCESS then
begin
  DbgPrint('FileWorks: File openeded'#13#10);
  g_szDataToAppend := ' using ZwWriteFile';
  RtnCode := ZwWriteFile(hFile, 0, nil, nil, @iosb, g_szDataToAppend,
                      strlen(g_szDataToAppend), nil, nil); 
文件的大小会自动增加。
9.9 截短文件
假设我们需要截短文件,去掉不需要的数据。在本例中,我为了简单起见,将文件缩减为原来的一半。
代码:
RtnCode := ZwOpenFile(@hFile, FILE_WRITE_DATA + SYNCHRONIZE,
                    @oa, @iosb, FILE_SHARE_READ,
                    FILE_SYNCHRONOUS_IO_NONALERT); 
我们用写访问来打开文件。
代码:
if RtnCode = STATUS_SUCCESS then
begin
RtnCode := ZwQueryInformationFile(hFile, @iosb, @fsi,
                                 sizeof(fsi),
                                 FileStandardInformation); 
我们在FILE_STANDARD_INFORMATION结构体的成员中可以获得文件当前的大小。
    
代码:
feofi.EndOfFile.HighPart := 0;
      feofi.EndOfFile.LowPart := (fsi.EndOfFile.LowPart) shr 1;
      RtnCode := ZwSetInformationFile(hFile, @iosb, @feofi,
                                  sizeof(feofi),
                                  FileEndOfFileInformation); 
使用信息类FileEndOfFileInformation,我们设置新的大小等于当前大小的一半。
9.10 删除文件与目录
小事一桩将c:恢复为初始的状态。令人奇怪的是,2000 DDK里没有提到存在ZwDeleteFile函数。在XP DDK中说系统终归提供了这个函数,但是是从Windows XP开始的。但事实并非如此。Windows 2000甚至Windows NT4中就有ZwDeleteFile。但对于文件的删除,我们有几种更为复杂的方法,而对目录的删除则借助于ZwDeleteFile。
代码:
RtnCode := ZwCreateFile(@hFile, _DELETE + SYNCHRONIZE,
                     @oa, @iosb, nil, 0, FILE_SHARE_DELETE,
                     FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT,
                     nil, 0); 
我们打开文件以进行删除。
代码:
if RtnCode = STATUS_SUCCESS then
begin
fdi.DeleteFile := True;
  RtnCode := ZwSetInformationFile(hFile, @iosb, @fdi,
                              sizeof(fdi),
                              FileDispositionInformation); 
使用信息类FileDispositionInformation并设置文件删除标志。这时在FILE_OBJECT结构体的DeletePending域中的值由FALSE变为TRUE。这表示文件被标记为删除。这样只要存在该文件的一个打开的句柄,文件就不会被删除。
ZwClose(hFile);
现在文件唯一的句柄被关闭,文件被删除。
ZwDeleteFile(@oa);
使用ZwDeleteFile删除目录很简单,我就不多说了。

9.11 列举目录内容
一般说来,有两种方法可以用来定义创建/打开的对象的名字,而文件却特别。访问命名对象可以使用完整的路径,也可以使用符号链接。这里我们只使用绝对路径。例如,路径\??\c:\FileWorks\test.txt就是绝对路径。在本例中,使用InitializeObjectAttributes宏填充InitializeObjectAttributes结构体,其形式如下:
代码:
InitializeObjectAttributes(oa, @g_usTemp,
                     OBJ_CASE_INSENSITIVE + OBJ_KERNEL_HANDLE,
                     0, nil);
 

倒数第二个参数RootDirectory为0。InitializeObjectAttributes的参数RootDirectory和该函数填充的OBJECT_ATTRIBUTES的同名域定义了目录容器对象的句柄。
代码:
OBJECT_ATTRIBUTES=record
. . .
  RootDirectory:THANDLE;
. . .
end; 
如果目录容器对象已经打开,则已经取得了句柄,就可以用相对于目录容器的路径来使用对象。这时目录容器的句柄应该放置在RootDirectory中。例如,如果我们已经打开了目录\??\c:\FileWorks\并将其句柄放入了变量hDirectory中,则对test.txt的文件路径我们可以如下使用:
代码:
RtlInitUnicodeString(g_usFileName, '\??\c:\FileWorks\test.txt');
InitializeObjectAttributes(a,@g_usFileName, 
OBJ_CASE_INSENSITIVE, hDirectory, nil);
 

在目录容器下就意味着目录不只是在磁盘上,还在对象管理器的名字空间中。
对于列出系统目录\%SystemRoot%\System32\Drivers\的内容,我们来使用相对路径。
代码:
RtlInitUnicodeString(g_usTemp, '\SystemRoot');
InitializeObjectAttributes(oa, @g_usTemp,
                     OBJ_CASE_INSENSITIVE + OBJ_KERNEL_HANDLE,
                     0, nil);
 

我们来填充OBJECT_ATTRIBUTES结构体,使用符号链接\SystemRoot这个是绝对路径。
代码:
RtnCode := ZwOpenFile(@hSystemRootDirectory, FILE_LIST_DIRECTORY + SYNCHRONIZE,
                        @oa, @iosb, FILE_SHARE_READ + FILE_SHARE_WRITE + FILE_SHARE_DELETE,
                        FILE_DIRECTORY_FILE + FILE_SYNCHRONOUS_IO_NONALERT); 
我们打开目录\%SystemRoot%\。FILE_LIST_DIRECTORY标志用于列举目录内容。
代码:
if RtnCode = STATUS_SUCCESS then
begin
  RtlInitUnicodeString(g_usTemp, 'system32\drivers');
  InitializeObjectAttributes(oa, @g_usTemp,
                       OBJ_CASE_INSENSITIVE + OBJ_KERNEL_HANDLE,
                       hSystemRootDirectory, nil); 
如果目录打开成功,从变量hSystemRootDirectory中我们可以取得其句柄,这个句柄将用作目录容器的句柄。记着OBJECT_ATTRIBUTES结构体使用相对路径“system32\drivers”。
代码:
RtnCode := ZwOpenFile(@hDriversDirectory, FILE_LIST_DIRECTORY + SYNCHRONIZE,
                          @oa, @iosb, FILE_SHARE_READ + FILE_SHARE_WRITE + FILE_SHARE_DELETE,
                          FILE_DIRECTORY_FILE + FILE_SYNCHRONOUS_IO_NONALERT);
if RtnCode = STATUS_SUCCESS then
begin
 

我们来用相对路径打开目录\%SystemRoot%\System32\Drivers\。
代码:
cb := sizeof(FILE_DIRECTORY_INFORMATION) + 256;
pfdi := ExAllocatePool(PagedPool, cb);
if pfdi <> nil then
begin
 

分配必要的缓冲区,在缓冲区中应放得下FILE_DIRECTORY_INFORMATION结构体和文件名。
代码:
RtlInitUnicodeString(g_usTemp, 'c*');
RtnCode := ZwQueryDirectoryFile(hDriversDirectory, 0, nil, nil, @iosb,
                             pfdi, cb, FileDirectoryInformation,
                             true, @g_usTemp, true);
 

我们开始列举目录文件,这里用的是信息类FileDirectoryInformation。函数ZwQueryDirectoryFile的原型如下。
代码:
NTSTATUS 
ZwQueryDirectoryFile(
IN HANDLE FileHandle,
IN HANDLE Event OPTIONAL,
IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,
IN PVOID ApcContext OPTIONAL,
OUT PIO_STATUS_BLOCK IoStatusBlock,
OUT PVOID FileInformation,
IN ULONG Length,
IN FILE_INFORMATION_CLASS FileInformationClass,
IN BOOLEAN ReturnSingleEntry,
IN PUNICODE_STRING FileName OPTIONAL,
IN BOOLEAN RestartScan
); 
但这个函数在2000 DDK中也没有被提到。XP DDK说这个函数只从Windows XP开始提供。这同样也不是真的。
我们令参数ReturnSingleEntry为TRUE,这就使ZwQueryDirectoryFile函数只返回一个文件的信息,而且是第一个文件的。参数FileName指向带有搜索条件“c*”的字符串,即只列出文件名以“c”开头的文件。这样能缩短输出的调试信息。在第一次调用ZwQueryDirectoryFile时,我们将参数RestartScan设为TRUE。这就使得ZwQueryDirectoryFile函数开始检查目录的内容。
while RtnCode <> STATUS_NO_MORE_FILES do
begin 

循环调用ZwQueryDirectoryFile直到ZwQueryDirectoryFile返回STATUS_NO_MORE_FILES,即目录中所有文件都已经列举。
代码:
if RtnCode = STATUS_SUCCESS then
begin
 

如果突然发现有文件的文件名超过256字节(实际上是不会的,因为驱动的文件名不会超过8个字符),ZwQueryDirectoryFile返回非STATUS_NO_MORE_FILES的错误代号。这时,我们只简单的过滤掉这个文件。
        
代码:
us.Length := pfdi^.FileNameLength;
            us.MaximumLength := pfdi^.FileNameLength;
            us.Buffer := PWideChar(@pfdi^.FileName);
            if RtlUnicodeStringToAnsiString(@_as, @us, true) = STATUS_SUCCESS then
            begin
              RtlTimeToTimeFields(@pfdi^.CreationTime, @tf);
              DbgPrint(' %s size=%d created on %d.%02d.%04d'#13#10,
                       _as.Buffer, pfdi^.EndOfFile.LowPart,
                       BYTE(tf.Day), BYTE(tf.Month), WORD(tf.Year));
              RtlFreeAnsiString(@_as);
            End;
 

格式化获得的信息,输出文件名、大小以及创建时间。
  
代码:
RtnCode := ZwQueryDirectoryFile(hDriversDirectory, 0, nil, nil, @iosb,
                                     pfdi, cb, FileDirectoryInformation,
                                     true, nil, false);
        end; {End While}
 

在调用ZwQueryDirectoryFile的循环中,参数ReturnSingleEntry、FileName和RestartScan分别为为TRUE、NULL和FALSE。这就使得ZwQueryDirectoryFile能继续列举文件。
代码:
ExFreePool(pfdi);
      end;
      ZwClose(hDriversDirectory);
    end else
    begin
      DbgPrint('FileWorks: Can''t open drivers directory. Status: %08X', RtnCode);
    end;
ZwClose(hSystemRootDirectory);
 

回收所有用过的资源。
以下是DebugView里输出的调试信息,你可以看到驱动程序正常工作并完成了相关的文件和目录操作。
代码:
FileWorks: Entering DriverEntry  
  
FileWorks: Creating \??\c:\FileWorks directory  
FileWorks: Directory created  
  
FileWorks: Creating \??\c:\FileWorks\test.txt file  
FileWorks: File created  
  
FileWorks: Opening file for writing  
FileWorks: File openeded  
FileWorks: File was written  
  
FileWorks: Opening file for changing attributes  
FileWorks: File openeded  
FileWorks: File attributes were: 00000020  
FileWorks: Now file marked as read-only  
  
FileWorks: Opening file for reading  
FileWorks: File openeded  
FileWorks: File content: \=Data can be written to an open file\=  
  
FileWorks: Opening file for changing attributes  
FileWorks: File openeded  
FileWorks: File attributes were: 00000021  
FileWorks: Now file can be written or deleted  
  
FileWorks: Opening file to append data  
FileWorks: File openeded  
FileWorks: Data appended to the file  
  
FileWorks: Opening file for reading  
FileWorks: File openeded  
FileWorks: File content: \=Data can be written to an open file using ZwWriteFile\=  
  
FileWorks: Opening file to truncate  
FileWorks: File openeded  
FileWorks: EOF was: 00000035  
FileWorks: File truncated to its half size  
  
FileWorks: Opening file for reading  
FileWorks: File openeded  
FileWorks: File content: \=Data can be written to an \=  
  
FileWorks: Opening file for deletion  
FileWorks: File openeded  
FileWorks: File has been marked for deletion  
FileWorks: It should be deleted when the last open handle is closed  
  
FileWorks: Directory should be deleted  
  
FileWorks: Opening directory to enumerate files  
  
FileWorks: ---------- Starting enumerate files ----------  
 cbidf2k.sys size=13952 created on 2.05.2008  
 cd20xrnt.sys size=7680 created on 2.05.2008  
 cdaudio.sys size=18688 created on 2.05.2008  
 cdfs.sys size=63744 created on 2.05.2008  
 cdr4_xp.sys size=2432 created on 5.06.2006  
 cdralw2k.sys size=2560 created on 5.06.2006  
 cdrom.sys size=62976 created on 2.05.2008  
 ch7xxnt5.dll size=15423 created on 2.05.2008  
 cinemst2.sys size=262528 created on 2.05.2008  
 classpnp.sys size=49536 created on 2.05.2008  
 cmbatt.sys size=13952 created on 2.05.2008  
 cmdide.sys size=6656 created on 2.05.2008  
 compbatt.sys size=10240 created on 2.05.2008  
 cpqarray.sys size=14976 created on 2.05.2008  
 cpqdap01.sys size=11776 created on 2.05.2008  
 crusoe.sys size=39552 created on 2.05.2008  
 cxthsfs2.cty size=129045 created on 2.05.2008  
FileWorks: ------------------------------------------------  
  
FileWorks: Leaving DriverEntry