首页 | 博客群 | 公社 | 专栏 | 论坛 | 图片 | 资讯 | 注册 | 帮助 | 博客联播 | 随机访问
Kmdtut 10---注册表- -| 回首页 | 2006年索引 | - -通过进程链枚举进程

Kmdtut 11---目录与文件

                                      

目录与文件

11.1 核心句柄表
11.2 FileWorks驱动程序源代码
11.3 创建目录与文件
11.4 文件对象
11.5 写入文件
11.6 修改文件属性
11.7 读取文件
11.8 向文件追加数据
11.9 截短文件
11.10 删除文件与目录
11.11 列举目录内容

 

源程序: KmdKit\examples\basic\FileWorks

提供对文件的读写功能是操作系统的一项重要任务。我们来看一下NT家族的操作系统都为我们提供了那些功能。

11.1 核心句柄表

在开始讨论本文的主题之前,我们先来讨论一个重要的问题,我们之前并未对其给予应有的注意。为了取得对象的句柄需要填充OBJECT_ATTRIBUTES结构体——我们已经做过很多遍了,其样子如下:

InitializeObjectAttributes addr oa, addr g_usName, OBJ_CASE_INSENSITIVE, NULL, NULL

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

从Windows 2000开始,在系统中有了专门的核心句柄表。在这个表中的句柄只能内核模式下的任意进程上下文中访问,与进程特有的句柄不同。甚至于,比如说如果在System进程的上下文、在DriverEntry函数中获得句柄,则就不能在用户进程上下文中使用对象。System进程实现了自己私有的句柄表,其与核心句柄表不同。

对于在核心句柄表中的句柄,需要在调用InitializeObjectAttributes宏时显式地设置OBJ_KERNEL_HANDLE标志,形式如下:

InitializeObjectAttributes addr oa, addr g_usName, OBJ_KERNEL_HANDLE, NULL, NULL

11.2 FileWorks驱动程序源代码

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

;@echo off
;goto make

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
;                                                                                                  
;  FileWorks - Пример различных операций с файлами.                                                
;                                                                                                  
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

.386
.model flat, stdcall
option casemap:none

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
;                               В К Л Ю Ч А Е М Ы Е    Ф А Й Л Ы                                   
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

include \masm32\include\w2k\ntstatus.inc
include \masm32\include\w2k\ntifs.inc
include \masm32\include\w2k\ntoskrnl.inc

includelib \masm32\lib\w2k\ntoskrnl.lib

include \masm32\Macros\Strings.mac

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
;                             Н Е И З М Е Н Я Е М Ы Е    Д А Н Н Ы Е                             &nbs; 
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

.const

CCOUNTED_UNICODE_STRING "\\??\\c:\\FileWorks\\test.txt", g_usFileName, 4
CCOUNTED_UNICODE_STRING "\\??\\c:\\FileWorks", g_usDirName, 4

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
;                                           К О Д                                                  
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

.code

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
;                                      CreateDirectory                                             
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

CreateDirectory proc

local oa:OBJECT_ATTRIBUTES
local iosb:IO_STATUS_BLOCK
local hDirectory:HANDLE

    ; 还记得吧, 传递给DbgPrint函数的用于格式化Unicode的代码(%C, %S, %lc, %ls, %wc, %ws, %wZ)只能在
    ; IRQL = PASSIVE_LEVEL下调用!
   
    invoke DbgPrint, $CTA0("\nFileWorks: Creating %ws directory\n"), g_usDirName.Buffer

    InitializeObjectAttributes addr oa, addr g_usDirName, \
                        OBJ_CASE_INSENSITIVE + OBJ_KERNEL_HANDLE, NULL, NULL

    invoke ZwCreateFile, addr hDirectory, SYNCHRONIZE, addr oa, addr iosb, 0, FILE_ATTRIBUTE_NORMAL, \
                        0, FILE_OPEN_IF, FILE_DIRECTORY_FILE + FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0
    .if eax == STATUS_SUCCESS
        .if iosb.Information == FILE_CREATED
            invoke DbgPrint, $CTA0("FileWorks: Directory created\n")
        .elseif iosb.Information == FILE_OPENED
            invoke DbgPrint, $CTA0("FileWorks: Directory exists and was opened\n")
        .endif
        invoke ZwClose, hDirectory
    .else
        invoke DbgPrint, $CTA0("FileWorks: Can't create directory. Status: %08X\n"), eax
    .endif
   
    ret

CreateDirectory endp

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
;                                        CreateFile                                                
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

CreateFile proc

local oa:OBJECT_ATTRIBUTES
local iosb:IO_STATUS_BLOCK
local hFile:HANDLE

    invoke DbgPrint, $CTA0("\nFileWorks: Creating %ws file\n"), g_usFileName.Buffer

    InitializeObjectAttributes addr oa, addr g_usFileName, \
                        OBJ_CASE_INSENSITIVE + OBJ_KERNEL_HANDLE, NULL, NULL

    invoke ZwCreateFile, addr hFile, SYNCHRONIZE, addr oa, addr iosb, 0, FILE_ATTRIBUTE_NORMAL, \
                        0, FILE_CREATE, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0
    .if eax == STATUS_SUCCESS
        invoke DbgPrint, $CTA0("FileWorks: File created\n")
        invoke ZwClose, hFile
    .else
        invoke DbgPrint, $CTA0("FileWorks: Can't create file. Status: %08X\n"), eax
    .endif
   
    ret

CreateFile endp

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
;                                            WriteFile                                             
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

WriteFile proc

local oa:OBJECT_ATTRIBUTES
local iosb:IO_STATUS_BLOCK
local hFile:HANDLE

    invoke DbgPrint, $CTA0("\nFileWorks: Opening file for writing\n")

    InitializeObjectAttributes addr oa, addr g_usFileName, \
                        OBJ_CASE_INSENSITIVE + OBJ_KERNEL_HANDLE, NULL, NULL
   
    invoke ZwCreateFile, addr hFile, FILE_WRITE_DATA + SYNCHRONIZE, addr oa, addr iosb, \
                        0, 0, FILE_SHARE_READ, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0
    .if eax == STATUS_SUCCESS
        invoke DbgPrint, $CTA0("FileWorks: File openeded\n")

        CTA0 "Data can be written to an open file", g_szData, 4

        invoke ZwWriteFile, hFile, 0, NULL, NULL, addr iosb, \
                        addr g_szData, sizeof g_szData - 1, NULL, NULL
        .if eax = STATUS_SUCCESS
            invoke DbgPrint, $CTA0("FileWorks: File was written\n")
        .else
            invoke DbgPrint, $CTA0("FileWorks: Can't write to the file. Status: %08X\n"), eax
        .endif

        invoke ZwClose, hFile
    .else
        invoke DbgPrint, $CTA0("FileWorks: Can't open file. Status: %08X\n"), eax
    .endif

    ret

WriteFile endp

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
;                                        MarkAsReadOnly                                            
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

MarkAsReadOnly proc
 
local oa:OBJECT_ATTRIBUTES
local iosb:IO_STATUS_BLOCK
local hFile:HANDLE
local fbi:FILE_BASIC_INFORMATION

    invoke DbgPrint, $CTA0("\nFileWorks: Opening file for changing attributes\n")

    InitializeObjectAttributes addr oa, addr g_usFileName, \
                        OBJ_CASE_INSENSITIVE + OBJ_KERNEL_HANDLE, NULL, NULL
   
    invoke ZwCreateFile, addr hFile, FILE_READ_ATTRIBUTES + FILE_WRITE_ATTRIBUTES + SYNCHRONIZE, \
                        addr oa, addr iosb, 0, 0, FILE_SHARE_READ, \
                        FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0
    .if eax == STATUS_SUCCESS
        invoke DbgPrint, $CTA0("FileWorks: File openeded\n")

        invoke ZwQueryInformationFile, hFile, addr iosb, addr fbi, sizeof fbi, FileBasicInformation
        .if eax == STATUS_SUCCESS
            invoke DbgPrint, $CTA0("FileWorks: File attributes were: %08X\n"), fbi.FileAttributes
            or fbi.FileAttributes, FILE_ATTRIBUTE_READONLY
            invoke ZwSetInformationFile, hFile, addr iosb, addr fbi, sizeof fbi, FileBasicInformation
            .if eax == STATUS_SUCCESS
                invoke DbgPrint, $CTA0("FileWorks: Now file marked as read-only\n")
            .else
                invoke DbgPrint, $CTA0("FileWorks: Can't change file attributes. Status: %08X\n"), eax
            .endif
        .else
            invoke DbgPrint, $CTA0("FileWorks: Can't query file attributes. Status: %08X\n"), eax
        .endif

        invoke ZwClose, hFile
    .else
        invoke DbgPrint, $CTA0("FileWorks: Can't open file. Status: %08X\n"), eax
    .endif

    ret

MarkAsReadOnly endp

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
;                                          ReadFile                                                
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

ReadFile proc

local oa:OBJECT_ATTRIBUTES
local iosb:IO_STATUS_BLOCK
local hFile:HANDLE
local p:PVOID
local cb:DWORD
local fsi:FILE_STANDARD_INFORMATION

    invoke DbgPrint, $CTA0("\nFileWorks: Opening file for reading\n")

    InitializeObjectAttributes addr oa, addr g_usFileName, \
                        OBJ_CASE_INSENSITIVE + OBJ_KERNEL_HANDLE, NULL, NULL

    invoke ZwOpenFile, addr hFile, FILE_READ_DATA + SYNCHRONIZE, addr oa, addr iosb, \
                FILE_SHARE_READ + FILE_SHARE_WRITE + FILE_SHARE_DELETE, FILE_SYNCHRONOUS_IO_NONALERT
    .if eax == STATUS_SUCCESS

        invoke DbgPrint, $CTA0("FileWorks: File openeded\n")

        invoke ZwQueryInformationFile, hFile, addr iosb, addr fsi, sizeof fsi, FileStandardInformation
        .if eax == STATUS_SUCCESS

            mov eax, fsi.EndOfFile.LowPart
            inc eax
            mov cb, eax

            invoke ExAllocatePool, PagedPool, cb
            .if eax != NULL
                mov p, eax

                invoke RtlZeroMemory, p, cb

                invoke ZwReadFile, hFile, 0, NULL, NULL, addr iosb, p, cb, 0, NULL
                .if eax == STATUS_SUCCESS
                    invoke DbgPrint, $CTA0("FileWorks: File content: \=%s\=\n"), p
                .else
                    invoke DbgPrint, $CTA0("FileWorks: Can't read from the file. Status: %08X\n"), eax
                .ndif

                invoke ExFreePool, p

            .else
                invoke DbgPrint, $CTA0("FileWorks: Can't allocate memory. Status: %08X\n"), eax
            .endif
        .else
            invoke DbgPrint, $CTA0("FileWorks: Can't query file size. Status: %08X\n"), eax
        .endif

        invoke ZwClose, hFile

    .else
        invoke DbgPrint, $CTA0("FileWorks: Can't open file. Status: %08X\n"), eax
    .endif

    ret

ReadFile endp

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
;                                        UnmarkAsReadOnly                                          
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

UnmarkAsReadOnly proc

local oa:OBJECT_ATTRIBUTES
local iosb:IO_STATUS_BLOCK
local hFile:HANDLE
local fbi:FILE_BASIC_INFORMATION

    invoke DbgPrint, $CTA0("\nFileWorks: Opening file for changing attributes\n")

    InitializeObjectAttributes addr oa, addr g_usFileName, \
                        OBJ_CASE_INSENSITIVE + OBJ_KERNEL_HANDLE, NULL, NULL
   
    invoke ZwCreateFile, addr hFile, FILE_READ_ATTRIBUTES + FILE_WRITE_ATTRIBUTES + SYNCHRONIZE, \
                        addr oa, addr iosb, 0, 0, FILE_SHARE_READ, FILE_OPEN, \
                        FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0
    .if eax == STATUS_SUCCESS
        invoke DbgPrint, $CTA0("FileWorks: File openeded\n")

        invoke ZwQueryInformationFile, hFile, addr iosb, addr fbi, sizeof fbi, FileBasicInformation
        .if eax == STATUS_SUCCESS
            invoke DbgPrint, $CTA0("FileWorks: File attributes were: %08X\n"), fbi.FileAttributes
            and fbi.FileAttributes, not FILE_ATTRIBUTE_READONLY
            invoke ZwSetInformationFile, hFile, addr iosb, addr fbi, sizeof fbi, FileBasicInformation
            .if eax == STATUS_SUCCESS
                invoke DbgPrint, $CTA0("FileWorks: Now file can be written or deleted\n")
            .else
                invoke DbgPrint, $CTA0("FileWorks: Can't change file attributes. Status: %08X\n"), eax
            .endif
        .else
            invoke DbgPrint, $CTA0("FileWorks: Can't query file attributes. Status: %08X\n"), eax
        .endif

        invoke ZwClose, hFile
    .else
        invoke DbgPrint, $CTA0("FileWorks: Can't open file. Status: %08X\n"), eax
    .endif

    ret

UnmarkAsReadOnly endp

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
;                                         AppendFile                                               
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

AppendFile proc

local oa:OBJECT_ATTRIBUTES
local iosb:IO_STATUS_BLOCK
local hFile:HANDLE

    invoke DbgPrint, $CTA0("\nFileWorks: Opening file to append data\n")

    InitializeObjectAttributes addr oa, addr g_usFileName, \
                        OBJ_CASE_INSENSITIVE + OBJ_KERNEL_HANDLE, NULL, NULL

    invoke ZwOpenFile, addr hFile, FILE_APPEND_DATA + SYNCHRONIZE, addr oa, addr iosb, \
                                    FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_NONALERT
    .if eax == STATUS_SUCCESS
        invoke DbgPrint, $CTA0("FileWorks: File openeded\n")

        CTA0 " using ZwWriteFile", g_szDataToAppend, 4

        invoke ZwWriteFile, hFile, 0, NULL, NULL, addr iosb, \
                        addr g_szDataToAppend, sizeof g_szDataToAppend - 1, NULL, NULL
        .if eax == STATUS_SUCCESS
            invoke DbgPrint, $CTA0("FileWorks: Data appended to the file\n")
        .else
            invoke DbgPrint, $CTA0("FileWorks: Can't append data to file. Status: %08X\n"), eax
        .endif

        invoke ZwClose, hFile
   .else
        invoke DbgPrint, $CTA0("FileWorks: Can't open file. Status: %08X\n"), eax
    .endif

    ret

AppendFile endp

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
;                            &nbp;           TruncateFile                                              
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

TruncateFile proc
 
local oa:OBJECT_ATTRIBUTES
local iosb:IO_STATUS_BLOCK
local hFile:HANDLE
local fsi:FILE_STANDARD_INFORMATION
local feofi:FILE_END_OF_FILE_INFORMATION

    invoke DbgPrint, $CTA0("\nFileWorks: Opening file to truncate\n")

    InitializeObjectAttributes addr oa, addr g_usFileName, \
                        OBJ_CASE_INSENSITIVE + OBJ_KERNEL_HANDLE, NULL, NULL

    invoke ZwOpenFile, addr hFile, FILE_WRITE_DATA + SYNCHRONIZE, addr oa, addr iosb, \
                        FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_NONALERT
    .if eax == STATUS_SUCCESS
        invoke DbgPrint, $CTA0("FileWorks: File openeded\n")

        invoke ZwQueryInformationFile, hFile, addr iosb, \
                        addr fsi, sizeof fsi, FileStandardInformation
        .if eax == STATUS_SUCCESS

            invoke DbgPrint, $CTA0("FileWorks: EOF was: %08X\n"), fsi.EndOfFile.LowPart

            and feofi.EndOfFile.HighPart, 0
            mov eax, fsi.EndOfFile.LowPart
            shr eax, 1
            mov feofi.EndOfFile.LowPart, eax
            invoke ZwSetInformationFile, hFile, addr iosb, \
                        addr feofi, sizeof feofi, FileEndOfFileInformation
            .if eax == STATUS_SUCCESS
                invoke DbgPrint, $CTA0("FileWorks: File truncated to its half size\n")
            .else
                invoke DbgPrint, $CTA0("FileWorks: Can't truncate file. Status: %08X\n"), eax      
            .endif

        .else
            invoke DbgPrint, $CTA0("FileWorks: Can't query file info. Status: %08X\n"), eax
        .endif

        invoke ZwClose, hFile
    .else
        invoke DbgPrint, $CTA0("FileWorks: Can't open file. Status: %08X\n"), eax
    .endif

    ret

TruncateFile endp

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
;                                         DeleteFile                                               
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

DeleteFile proc

local oa:OBJECT_ATTRIBUTES
local iosb:IO_STATUS_BLOCK
local hFile:HANDLE
local fdi:FILE_DISPOSITION_INFORMATION

    invoke DbgPrint, $CTA0("\nFileWorks: Opening file for deletion")

    InitializeObjectAttributes addr oa, addr g_usFileName, \
                        OBJ_CASE_INSENSITIVE + OBJ_KERNEL_HANDLE, NULL, NULL

    invoke ZwCreateFile, addr hFile, DELETE + SYNCHRONIZE, addr oa, addr iosb, \
                        0, 0, FILE_SHARE_DELETE, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0
    .if eax == STATUS_SUCCESS
        invoke DbgPrint, $CTA0("FileWorks: File openeded\n")

        mov fdi.DeleteFile, TRUE
        invoke ZwSetInformationFile, hFile, addr iosb, addr fdi, sizeof fdi, FileDispositionInformation
        .if eax == STATUS_SUCCESS
            invoke DbgPrint, $CTA0("FileWorks: File has been marked for deletion\n")
            invoke DbgPrint, $CTA0("FileWorks: It should be deleted when the last open handle is closed\n")
        .else
            invoke DbgPrint, $CTA0("FileWorks: Can't mark file for deletion. Status: %08X\n"), eax
        .endif

        invoke ZwClose, hFile
    .else
        invoke DbgPrint, $CTA0("FileWorks: Can't open file. Status: %08X\n"), eax
    .endif

    ret

DeleteFile endp

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
;                                       DeleteDirectory                                            
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

DeleteDirectory proc

local oa:OBJECT_ATTRIBUTES
local iosb:IO_STATUS_BLOCK
local hDirectory:HANDLE

    InitializeObjectAttributes addr oa, addr g_usDirName, \
                        OBJ_CASE_INSENSITIVE + OBJ_KERNEL_HANDLE, NULL, NULL

    invoke ZwDeleteFile, addr oa
    .if eax == SATUS_SUCCESS
        invoke DbgPrint, $CTA0("\nFileWorks: Directory should be deleted\n")           
    .else
        invoke DbgPrint, $CTA0("\nFileWorks: Can't delete directory. Status: %08X\n"), eax
    .endif

    ret

DeleteDirectory endp

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
;                                      EnumerateFiles                                              
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

EnumerateFiles proc uses esi

local status:NTSTATUS
local oa:OBJECT_ATTRIBUTES
local hSystemRootDirectory:HANDLE
local hDriversDirectory:HANDLE
local as:ANSI_STRING
local us:UNICODE_STRING
local iosb:IO_STATUS_BLOCK
local tf:TIME_FIELDS
local cb:DWORD
local pfdi:PFILE_DIRECTORY_INFORMATION

    invoke DbgPrint, $CTA0("\nFileWorks: Opening directory to enumerate files\n")
   
    InitializeObjectAttributes addr oa, $CCOUNTED_UNICODE_STRING("\\SystemRoot"), \
                                OBJ_CASE_INSENSITIVE + OBJ_KERNEL_HANDLE, NULL, NULL

    invoke ZwOpenFile, addr hSystemRootDirectory, FILE_LIST_DIRECTORY + SYNCHRONIZE, addr oa, \
                        addr iosb, FILE_SHARE_READ + FILE_SHARE_WRITE + FILE_SHARE_DELETE, \
                        FILE_DIRECTORY_FILE + FILE_SYNCHRONOUS_IO_NONALERT
    .if eax == STATUS_SUCCESS

        InitializeObjectAttributes addr oa, $CCOUNTED_UNICODE_STRING("system32\\drivers"), \
                            OBJ_CASE_INSENSITIVE + OBJ_KERNEL_HANDLE, hSystemRootDirectory, NULL

        invoke ZwOpenFile, addr hDriversDirectory, FILE_LIST_DIRECTORY + SYNCHRONIZE, addr oa, \
                            addr iosb, FILE_SHARE_READ + FILE_SHARE_WRITE + FILE_SHARE_DELETE, \
                            FILE_DIRECTORY_FILE + FILE_SYNCHRONOUS_IO_NONALERT
        .if eax == STATUS_SUCCESS

            mov cb, sizeof FILE_DIRECTORY_INFORMATION + 256

            invoke ExAllocatePool, PagedPool, cb
            .if eax != NULL

                mov pfdi, eax
                mov esi, eax
                assume esi:ptr FILE_DIRECTORY_INFORMATION

                invoke DbgPrint, \
                        $CTA0("\nFileWorks: ---------- Starting enumerate files ----------\n")

                invoke ZwQueryDirectoryFile, hDriversDirectory, NULL, NULL, NULL, addr iosb, \
                            esi, cb, FileDirectoryInformation, \
                            TRUE, $CCOUNTED_UNICODE_STRING("c*"), TRUE

                .while eax != STATUS_NO_MORE_FILES

                    .if ( eax == STATUS_SUCCESS )
 
                        mov eax, [esi].FileNameLength
                        mov us._Length, ax
                        mov us.MaximumLength, ax
                        lea eax, [esi].FileName
                        mov us.Buffer, eax
                        invoke RtlUnicodeStringToAnsiString, addr as, addr us, TRUE
                        .if eax == STATUS_SUCCESS

                            invoke RtlTimeToTimeFields, addr [esi].CreationTime, addr tf
                            movzx eax, tf.Day
                            movzx ecx, tf.Month
                            movzx edx, tf.Year

                            invoke DbgPrint, $CTA0("    %s   size=%d   created on %d.%02d.%04d\n"), \
                                        as.Buffer, [esi].EndOfFile.LowPart, eax, ecx, dx

                            invoke RtlFreeAnsiString, addr as
                        .endif

                    .endif

                    invoke ZwQueryDirectoryFile, hDriversDirectory, NULL, NULL, NULL, addr iosb, \
                                esi, cb, FileDirectoryInformation, \
                                TRUE, NULL, FALSE
                .endw
                invoke DbgPrint, \
                    $CTA0("FileWorks: ------------------------------------------------\n")

                assume esi:nothing
                invoke ExFreePool, pfdi
            .endif
            invoke ZwClose, hDriversDirectory
        .else
            invoke DbgPrint, $CTA0("FileWorks: Can't open drivers directory. Status: %08X\n"), eax
        .endif
        invoke ZwClose, hSystemRootDirectory
    .else
        invoke DbgPrint, $CTA0("FileWorks: Can't open system root directory. Status: %08X\n"), eax
    .endif

    ret

EnumerateFiles endp

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
;                                       DriverEntry                                                
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

DriverEntry proc pDriverObject:PDRIVER_OBJECT, pusRegistryPath:PUNICODE_STRING

    invoke DbgPrint, $CTA0("\nFileWorks: Entering DriverEntry\n")

    invoke CreateDirectory
    invoke CreateFile
    invoke WriteFile
    invoke MarkAsReadOnly
    invoke ReadFile
    invoke UnmarkAsReadOnly
    invoke AppendFile
    invoke ReadFile
    invoke TruncateFile
    invoke ReadFile
    invoke DeleteFile
    invoke DeleteDirectory
    invoke EnumerateFiles

    invoke DbgPrint, $CTA0("\nFileWorks: Leaving DriverEntry\n")

    mov eax, STATUS_DEVICE_CONFIGURATION_ERROR
    ret

DriverEntry endp

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
;                                                                                                  
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

end DriverEntry

:make

set drv=FileWorks

\masm32\bin\ml /nologo /c /coff %drv%.bat
\masm32\bin\link /nologo /driver /base:0x10000 /align:32 /out:%drv%.sys /subsystem:native %drv%.obj

del %drv%.obj

echo.
pause

在ntddk.inc中没有几个我们需要的常量和结构体。

include \masm32\include\w2k\ntifs.inc

在用于处理文件系统驱动的Installable File System (IFS) Kit中,有一个ntifs.h头文件,而我们这里则要包含ntifs.inc文件。在1.5版的KmdKit中我加入了这个文件。

11.3 创建目录与文件

    InitializeObjectAttributes addr oa, addr g_usDirName, \
                        OBJ_CASE_INSENSITIVE + OBJ_KERNEL_HANDLE, NULL, NULL

填充OBJECT_ATTRIBUTES结构体,不要忘了OBJ_KERNEL_HANDLE标志。我想要强调的是,在本例中这个操作并不是必需的,因为我们不会在其它进程中使用这些句柄。但是因为FileWorks驱动程序的函数可以很容易的移植到使用任意进程上下文的程序中,所以我决定设置这个标志。如果在您的程序中有驱动和用户进程的共用的句柄,则不应设置此标志。如果只在自己一个进程上下文中使用对象,也不必设置OBJ_KERNEL_HANDLE标志。

创建目录和文件用的都是ZwCreateFile函数。从系统角度看,目录也是文件,所以创建目录的函数与创建文件的函数没有本质上的差别。所以,创建目录与创建文件使用同一个函数,只是创建目录要用FILE_DIRECTORY_FILE标志。

    invoke ZwCreateFile, addr hDirectory, SYNCHRONIZE, addr oa, addr iosb, 0, FILE_ATTRIBUTE_NORMAL, \
                        0, FILE_OPEN_IF, FILE_DIRECTORY_FILE + FILE_SYNCHRONOUS_IO_NONALERT, NULL, 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         &nsp;     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 eax == STATUS_SUCCESS
        .if iosb.Information == FILE_CREATED
        .elseif iosb.Information == FILE_OPENED
        .endif

正如我所讲的,在IO_STATUS_BLOCK结构体中会有额外的信息。

还记得我们是如何处理I/O请求的(见前面的章节)。例如,在驱动程序SharingMemory(第9章)中对IRP_MJ_CREATE和IRP_MJ_CLOSE的处理如下:

mov eax, pIrp
mov (_IRP PTR [eax]).IoStatus.Status, STATUS_SUCCESS
and (_IRP PTR [eax]).IoStatus.Information, 0
fastcall IofCompleteRequest, pIrp, IO_NO_INCREMENT

本例的驱动程序也大致相同,即将完成创建文件的请求。只有放入IO_STATUS_BLOCK结构体的域中的值会依赖于请求的类型。

因为我们定义了FILE_OPEN_IF标志,通过iosb.Information的值,我们可以知道是该建立新的目录还是因该目录已经存在而将其打开。

        invoke ZwClose, hDirectory
    .endif

我再重复一遍,在内核模式下一定要显式地关闭所有打开的句柄。

    invoke ZwCreateFile, addr hFile, SYNCHRONIZE, addr oa, addr iosb, 0, FILE_ATTRIBUTE_NORMAL, \
                        0, FILE_CREATE, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0

如您所见,文件的创建实际上是相同的,只是要去掉FILE_DIRECTORY_FILE标志。而FILE_CREATE标志我将其用于多种情况。它表示只可以创建文件。如果文件已经存在,则对ZwCreateFile会以失败结束。

11.4 文件对象

每一个打开的文件句柄都对应着一个文件对象(file object),在内核内存区中有FILE_OBJECT结构体。

FILE_OBJECT STRUCT                              ; sizeof = 070h
    _Type                   SWORD       ?       ; 0000h  IO_TYPE_FILE
    _Size                   SWORD       ?       ; 0002h
    DeviceObject            PVOID       ?       ; 0004h  PTR DEVICE_OBJECT
    Vpb                     PVOID       ?       ; 0008h  PTR VPB
    FsContext               PVOID       ?       ; 000Ch
    FsContext2              PVOID       ?       ; 0010h
    SectionObjectPointer    PVOID       ?       ; 0014h  PTR SECTION_OBJECT_POINTERS
    PrivateCacheMap         PVOID       ?       ; 0018h
    FinalStatus             SDWORD      ?       ; 001Ch
    RelatedFileObject       PVOID       ?       ; 0020h  PTR FILE_OBJECT
    LockOperation           BYTE        ?       ; 0024h  BOOLEAN
    DeletePending           BYTE        ?       ; 0025h  BOOLEAN
    ReadAccess              BYTE        ?       ; 0026h  BOOLEAN
    WriteAccess             BYTE        ?       ; 0027h  BOOLEAN
    DeleteAccess            BYTE        ?       ; 0028h  BOOLEAN
    SharedRead              BYTE        ?       ; 0029h  BOOLEAN
    SharedWrite             BYTE        ?       ; 002Ah  BOOLEAN
    SharedDelete            BYTE        ?       ; 002Bh  BOOLEAN
    Flags                   DWORD       ?       ; 002Ch
    FileName                UNICODE_STRING  <>  ; 0030h
    CurrentByteOffset       LARGE_INTEGER   <>  ; 0038h
    Waiters                 DWORD       ?       ; 0040h
    Busy                    DWORD       ?       ; 0044h
    LastLock     &nbs;          PVOID       ?       ; 0048h
    _Lock                   KEVENT      <>      ; 004Ch
    Event                   KEVENT      <>      ; 005Ch
    CompletionContext       PVOID       ?       ; 006Ch  PTR IO_COMPLETION_CONTEXT
FILE_OBJECT ENDS
PFILE_OBJECT typedef ptr FILE_OBJECT

例如,我们可以两次打开同一个文件,但是两次的访问请求却不相同:第一次读(FILE_READ_DATA)而第二次写(FILE_WRITE_DATA)。结果内核会建立两个FILE_OBJECT结构体,每一个都对应于自己相应的文件句柄。但是两个句柄和相对应的两个FILE_OBJECT结构体都对应着同一个磁盘文件。第一个FILE_OBJECT结构体将设置ReadAccess域,第二个则设置WriteAccess域。例如,试图写入第一个句柄指向的文件时,系统会发现域WriteAccess = FALSE并结束请求。

DeviceObject域将包含指向设备对象\Device\HarddiskVolume1的指针,因为这个设备是对\??\c:符号链接,我们创建文件时在文件名中使用了\??\c:。

在读完本文后您可能会进行文件操作的实验并会发现系统是如何填充并管理FILE_OBJECT结构体的。如果在内存中定位它时发生问题,可以使用ObReferenceObjectByHandle函数,形式大致如下:

local pFileObject:PFILE_OBJECT
. . .
invoke ObReferenceObjectByHandle, hFile, FILE_READ_DATA, NULL, KernelMode, addr pFileObject, NULL
.if eax == STATUS_SUCCESS
    ; pFileObject指向对应于hFile的FILE_OBJECT
    fastcall ObfDereferenceObject, pFileObject
.endif

ObReferenceObjectByHandle向参数pFileObject中返回指向对应于该文件句柄的FILE_OBJECT结构体的指针。这时,引用计数会增一。之后一定要调用ObfDereferenceObject来使其复原。

11.5 写入文件

到这里我们已经有了大小为0目录和文件。该向文件中写点东西了。

    invoke ZwCreateFile, addr hFile, FILE_WRITE_DATA + SYNCHRONIZE, addr oa, addr iosb, \
                        0, 0, FILE_SHARE_READ, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0

借助于ZwCreateFile不止可以创建文件,还可以打开已有的文件(恰好,不只是文件,其它某些对象也是这样)。为此需要指定标志FILE_OPEN。

为了写入文件,需要相应的访问权——使用FILE_WRITE_DATA。

在头文件中定义了几个常量以用于对文件的一般访问。例如,FILE_GENERIC_WRITE不仅允许向文件中写入数据,还可以更改其属性并添加数据。

FILE_GENERIC_WRITE equ (STANDARD_RIGHTS_WRITE or FILE_WRITE_DATA or FILE_WRITE_ATTRIBUTES or FILE_WRITE_EA or FILE_APPEND_DATA or SYNCHRONIZE)

但对于给定的情况我总是只使用最低限度必需的标志,因为不管是否需要总是使用例如FILE_ALL_ACCESS之类的标志并不是一种好的编程习惯。

显然,当我们写文件,其它的程序不应向该文件写入。使用FILE_SHARE_READ标志,则没有程序能写入或是删除文件,而只能读取文件。

    .if eax == STATUS_SUCCESS

        CTA0 "Data can be written to an open file", g_szData, 4

        invoke ZwWriteFile, hFile, 0, NULL, NULL, addr iosb, \
                        addr g_szData, sizeof g_szData - 1, NULL, NULL

函数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结构体指针——在该结构体中将放置额外的信息(其中包括向文件写入的字节数)、指向需要写入文件的数据的指针和其大小。

11.6 修改文件属性

我们需要防止我们的文件被删除。

    invoke ZwCreateFile, addr hFile, FILE_READ_ATTRIBUTES + FILE_WRITE_ATTRIBUTES + SYNCHRONIZE, \
                        addr oa, addr iosb, 0, 0, FILE_SHARE_READ, \
                        FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0

标志FILE_READ_ATTRIBUTES和FILE_WRITE_ATTRIBUTES用于取得和更改相应的文件属性。

    .if eax == STATUS_SUCCESS
        invoke ZwQueryInformationFile, hFile, addr iosb, addr fbi, sizeof fbi, FileBasicInformation
        .if eax == STATUS_SUCCESS

我们需要建立只读属性,但由于文件有其它的属性需要保留,故有:

            or fbi.FileAttributes, FILE_ATTRIBUTE_READONLY
            invoke ZwSetInformationFile, hFile, addr iosb, addr fbi, sizeof fbi, FileBasicInformation

要向文件属性中添加属性,我们必须要用标志。当需要根改文件时,我们用UnmarkAsReadOnly函数去掉这个属性,有:

            and fbi.FileAttributes, not FILE_ATTRIBUTE_READONLY


11.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
    );

    invoke ZwOpenFile, addr hFile, FILE_READ_DATA + SYNCHRONIZE, addr oa, addr 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 eax == STATUS_SUCCESS
        invoke ZwQueryInformationFile, hFile, addr iosb, addr fsi, sizeof fsi, FileStandardInformation
        .if eax == STATUS_SUCCESS

我们以准备好读取文件的所有数据,但是用于这个请求的缓冲区的大小依赖于文件的大小。文件的大小可以用信息类FileStandardInformation由函数ZwQueryInformationFile得,并将其传递给FILE_BASIC_INFORMATION指针。

            mov eax, fsi.EndOfFile.LowPart
            inc eax
            mov cb, eax

我们来为最后的零添加一个字节。

            invoke ExAllocatePool, PagedPool, cb
            .if eax != NULL
                mov p, eax

                invoke RtlZeroMemory, p, cb

                invoke ZwReadFile, hFile, 0, NULL, NULL, addr iosb, p, cb, 0, NULL
                .if eax == STATUS_SUCCESS
                    invoke DbgPrint, $CTA0("FileWorks: File content: \=%s\=\n"), p
                .endif

                invoke ExFreePool, p
            .endif

分配所需的缓冲区,将其清零并传递给ZwReadFile函数。这个函数的原型与ZwWriteFile函数的原型类似,只是缓冲区用处不同。因为在使用之前我们清零了缓冲区,并且其大小比文件的内容多一个字节,所以不会发生将其内容输出到调试信息的问题。

11.8 向文件追加数据

向文件中追加数据有几种方法。可以用标志FILE_WRITE_DATA将其打开,在当前位置建立指向文件末尾的指针,在函数ZwWriteFile的参数ByteOffset中传递偏移量并写入数据。指示文件当前位置的指针保存在FILE_OBJECT.CurrentByteOffset中。我们还可以用标志FILE_APPEND_DATA打开文件,文件当前位置指针会自动指向其末尾。

    invoke ZwOpenFile, addr hFile, FILE_APPEND_DATA + SYNCHRONIZE, addr oa, addr iosb, \
                                    FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_NONALERT
    .if eax == STATUS_SUCCESS

        CTA0 " using ZwWriteFile", g_szDataToAppend, 4

        invoke ZwWriteFile, hFile, 0, NULL, NULL, addr iosb, \
                        addr g_szDataToAppend, sizeof g_szDataToAppend - 1, NULL, NULL

文件的大小会自动增加。

11.9 截短文件

假设我们需要截短文件,去掉不需要的数据。在本例中,我为了简单起见,将文件缩减为原来的一半。

    invoke ZwOpenFile, addr hFile, FILE_WRITE_DATA + SYNCHRONIZE, addr oa, addr iosb, \
                        FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_NONALERT

我们用写访问来打开文件。

    .if eax == STATUS_SUCCESS
        invoke ZwQueryInformationFile, hFile, addr iosb, \
                        addr fsi, sizeof fsi, FileStandardInformation
        .if eax == STATUS_SUCCESS

我们在FILE_STANDARD_INFORMATION结构体的成员中可以获得文件当前的大小。

            and feofi.EndOfFile.HighPart, 0
            mov eax, fsi.EndOfFile.LowPart
            shr eax, 1
            mov feofi.EndOfFile.LowPart, eax
            invoke ZwSetInformationFile, hFile, addr iosb, \
                        addr feofi, sizeof feofi, FileEndOfFileInformation

使用信息类FileEndOfFileInformation,我们设置新的大小等于当前大小的一半。

11.10 删除文件与目录

小事一桩——将c:恢复为初始的状态。令人奇怪的是,2000 DDK里没有提到存在ZwDeleteFile函数。在XP DDK中说系统终归提供了这个函数,但是是从Windows XP开始的。但事实并非如此。Windows 2000甚至Windows NT4中就有ZwDeleteFile。但对于文件的删除,我们用几中更为复杂的方法,而对目录的删除则借助于ZwDeleteFile。

    invoke ZwCreateFile, addr hFile, DELETE + SYNCHRONIZE, addr oa, addr iosb, \
                        0, 0, FILE_SHARE_DELETE, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0

我们打开要文件以进行删除。

    .if eax == STATUS_SUCCESS

        mov fdi.DeleteFile, TRUE
        invoke ZwSetInformationFile, hFile, addr iosb, addr fdi, sizeof fdi, FileDispositionInformation

使用信息类FileDispositionInformation并设置文件删除标志。这时在FILE_OBJECT结构体的DeletePending域中的值由FALSE变为TRUE。这表示文件被标记为删除。这样只要存在该文件的一个打开的句柄,文件就不会被删除。

        invoke ZwClose, hFile

现在文件唯一的句柄被关闭,文件被删除。

    invoke ZwDeleteFile, addr oa

使用ZwDeleteFile删除目录很简单,我就不多说了。

11.11 列举目录内容

一般说来,有两种方法可以用来定义创建/打开的对象的名字,而文件却特别。访问命名对象可以使用完整的路径,也可以使用符号链接。这里我们只使用绝对路径。例如,路径\??\c:\FileWorks\test.txt就是绝对路径。在本例中,使用InitializeObjectAttributes宏填充InitializeObjectAttributes结构体,其形式如下:

InitializeObjectAttributes addr oa, $CCOUNTED_UNICODE_STRING("\??\c:\FileWorks\test.txt"), OBJ_CASE_INSENSITIVE, NULL, NULL

倒数第二个参数RootDirectory为NULL。宏InitializeObjectAttributes的参数RootDirectory和该宏填充的OBJECT_ATTRIBUTES的同名域定义了目录容器对象的句柄。

OBJECT_ATTRIBUTES STRUCT
. . .
    RootDirectory               HANDLE          ?
. . .
OBJECT_ATTRIBUTES ENDS

如果目录容器对象已经打开,则已经取得了句柄,就可以用相对于目录容器的路径来使用对象。这时目录容器的句柄应该放置在RootDirectory中。例如,如果我们已经打开了目录\??\c:\FileWorks\并将其句柄放入了变量hDirectory中,则对test.txt的文件路径我们可以如下使用:

InitializeObjectAttributes addr oa, $CCOUNTED_UNICODE_STRING("test.txt"), OBJ_CASE_INSENSITIVE, hDirectory, NULL

在目录容器下就意味着目录不只是在磁盘上,还在对象管理器的名字空间中。

对于列出系统目录\%SystemRoot%\System32\Drivers\的内容,我们来使用相对路径。

    InitializeObjectAttributes addr oa, $CCOUNTED_UNICODE_STRING("\\SystemRoot"), \
                                OBJ_CASE_INSENSITIVE + OBJ_KERNEL_HANDLE, NULL, NULL

我们来填充OBJECT_ATTRIBUTES结构体,使用符号链接\SystemRoot——这个是绝对路径。

    invoke ZwOpenFile, addr hSystemRootDirectory, FILE_LIST_DIRECTORY + SYNCHRONIZE, addr oa, \
               &nsp;        addr iosb, FILE_SHARE_READ + FILE_SHARE_WRITE + FILE_SHARE_DELETE, \
                        FILE_DIRECTORY_FILE + FILE_SYNCHRONOUS_IO_NONALERT

我们打开目录\%SystemRoot%\。FILE_LIST_DIRECTORY标志用于列举目录内容。

    .if eax == STATUS_SUCCESS

        InitializeObjectAttributes addr oa, $CCOUNTED_UNICODE_STRING("system32\\drivers"), \
                            OBJ_CASE_INSENSITIVE + OBJ_KERNEL_HANDLE, hSystemRootDirectory, NULL

如果目录打开成功,从变量hSystemRootDirectory中我们可以取得其句柄,这个句柄将用作目录容器的句柄。记着OBJECT_ATTRIBUTES结构体使用相对路径“system32\drivers”。

        invoke ZwOpenFile, addr hDriversDirectory, FILE_LIST_DIRECTORY + SYNCHRONIZE, addr oa, \
                            addr iosb, FILE_SHARE_READ + FILE_SHARE_WRITE + FILE_SHARE_DELETE, \
                            FILE_DIRECTORY_FILE + FILE_SYNCHRONOUS_IO_NONALERT
        .if eax == STATUS_SUCCESS

我们来用相对路径打开目录\%SystemRoot%\System32\Drivers\。

            mov cb, sizeof FILE_DIRECTORY_INFORMATION + 256

            invoke ExAllocatePool, PagedPool, cb
            .if eax != NULL

分配必要的缓冲区,在缓冲区中应放得下FILE_DIRECTORY_INFORMATION结构体和文件名。

                mov pfdi, eax
                mov esi, eax
                assume esi:ptr FILE_DIRECTORY_INFORMATION

                invoke ZwQueryDirectoryFile, hDriversDirectory, NULL, NULL, NULL, addr iosb, \
                            esi, cb, FileDirectoryInformation, \
                            TRUE, $CCOUNTED_UNICODE_STRING("c*"), 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 eax != STATUS_NO_MORE_FILES

循环调用ZwQueryDirectoryFile直到ZwQueryDirectoryFile返回STATUS_NO_MORE_FILES,即目录中所有文件都已经列举。

                    .if ( eax == STATUS_SUCCESS )

如果突然发现有文件的文件名超过256字节(实际上是不会的,因为驱动的文件名不会超过8个字符),ZwQueryDirectoryFile返回非STATUS_NO_MORE_FILES的错误代号。这时,我们只简单的过滤掉这个文件。

                        mov eax, [esi].FileNameLength
                        mov us._Length, ax
                        mov us.MaximumLength, ax
                        lea eax, [esi].FileName
                        mov us.Buffer, eax
                        invoke RtlUnicodeStringToAnsiString, addr as, addr us, TRUE
                        .if eax == STATUS_SUCCESS

                            invoke RtlTimeToTimeFields, addr [esi].CreationTime, addr tf
                            movzx eax, tf.Day
                            movzx ecx, tf.Month
                            movzx edx, tf.Year

                            invoke DbgPrint, $CTA0("    %s   size=%d   created on %d.%02d.%04d\n"), \
   &bsp;                                    as.Buffer, [esi].EndOfFile.LowPart, eax, ecx, edx

                            invoke RtlFreeAnsiString, addr as
                        .endif

格式化获得的信息,输出文件名、大小以及创建时间。

                    .endif

                    invoke ZwQueryDirectoryFile, hDriversDirectory, NULL, NULL, NULL, addr iosb, \
                                esi, cb, FileDirectoryInformation, \
                                TRUE, NULL, FALSE
                .endw

在调用ZwQueryDirectoryFile的循环中,参数ReturnSingleEntry、FileName和RestartScan分别为为TRUE、NULL和FALSE。这就使得ZwQueryDirectoryFile能继续列举文件。

                invoke ExFreePool, pfdi
            .endif
            invoke ZwClose, hDriversDirectory
        .endif
        invoke ZwClose, hSystemRootDirectory
    .endif

回收所有用过的资源。

--------------------------------------------------------------------------------

Copyright © 2002-2004 Four-F, four-f@mail.ru

董岩 译
http://greatdong.blog.edu.cn

【作者: D哥】【访问统计:】【2006年02月6日 星期一 16:42】【注册】【打印

搜索

Google

Trackback

你可以使用这个链接引用该篇文章 http://publishblog.blogchina.com/blog/tb.b?diaryID=4389666

回复

验证码:   
评论内容: