2014年6月4日星期三

驱动中枚举/遍历文件

#include <ntifs.h>
#include <windef.h>

#define TAG 'tset' //test

/*
文件名:ZwQueryDirectoryFile.C
功能:在驱动中遍历/枚举文件。

看似一个简单不值一看的功能,花费了我一天的时间,也许我太笨,所以有此记录。

made by correy
made at 2014.06.04
homepage:http://correy.webs.com
*/

DRIVER_UNLOAD Unload;
VOID Unload(__in PDRIVER_OBJECT DriverObject)
{  
   
}


NTSTATUS ZwEnumerateFile1(IN UNICODE_STRING * directory)// ,FILE_INFORMATION_CLASS fic
    /*
    功能:枚举目录下的文件。没有递归。

    输入参数:可以是路径,句柄,文件对象,这个可以修改,但不能是文件路径。

    路径的格式最好是:L"\\??\\C:\\Windows");或者"\Device\HarddiskVolume1\XXX  \\DosDevices\\C:\\,不然还得转换,结构最好用的UNICODE_STRING,这样安全。

    这个方法的思路是一下读取了一个目录下的所有信息。优缺点是不言而喻的,就是所需的内存的大小,你知道吗?

    其实,ZwEnumerateFile这个函数没有获取所需的内存的大小的功能,一个思路是结构的大小加路径的大小。
    */
{
    NTSTATUS status = STATUS_UNSUCCESSFUL;
    OBJECT_ATTRIBUTES ob;
    HANDLE FileHandle = 0;
    IO_STATUS_BLOCK  IoStatusBlock = {0};
    PVOID FileInformation = 0;
    ULONG Length = sizeof (FILE_DIRECTORY_INFORMATION);//这个数设置的太小会导致ZwQueryDirectoryFile蓝屏。
    FILE_DIRECTORY_INFORMATION * fibdi = 0;

    InitializeObjectAttributes(&ob, directory, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, 0, 0);
    status = ZwOpenFile(&FileHandle, GENERIC_READ | SYNCHRONIZE, &ob, &IoStatusBlock, FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_NONALERT | FILE_DIRECTORY_FILE);
    if (!NT_SUCCESS (status))
    {
        KdPrint(("ZwOpenFile fail with 0x%x.\n", status));
        if ( status == STATUS_OBJECT_NAME_NOT_FOUND || IoStatusBlock.Information == FILE_DOES_NOT_EXIST)  {
            KdPrint(("file does not exist\n"));
        }
        return status;
    }
   
    do
    {
        if (FileInformation)
        {
            ExFreePoolWithTag(FileInformation, TAG);
            FileInformation = NULL;
        }

        FileInformation = ExAllocatePoolWithTag(NonPagedPool, Length, TAG);
        if (FileInformation == NULL) {
            status = STATUS_UNSUCCESSFUL;
            DbgPrint("发生错误的文件为:%s, 代码行为:%d\n", __FILE__, __LINE__);
            ZwClose(FileHandle);
            return status;
        }
        RtlZeroMemory(FileInformation, Length);

        status = ZwQueryDirectoryFile(FileHandle, NULL, NULL, NULL, &IoStatusBlock, FileInformation, Length, FileDirectoryInformation, FALSE, NULL, TRUE);
        if (!NT_SUCCESS (status))
        {
            KdPrint(("ZwQueryDirectoryFile fail with 0x%x.\n", status));//STATUS_BUFFER_TOO_SMALL == C0000023
           
            //return status;
        }

        Length *= 2;
    } while (!NT_SUCCESS (status));

    for (fibdi = FileInformation ; ; fibdi = (FILE_DIRECTORY_INFORMATION *)((char *)fibdi + fibdi->NextEntryOffset))
    {
        UNICODE_STRING FileName ={0};

        if (FILE_ATTRIBUTE_DIRECTORY == fibdi->FileAttributes)
        {
            //这里可以考虑递归。这里放弃了文件夹的显示。
            continue;
        }

        FileName.Buffer = fibdi->FileName;
        FileName.Length = (USHORT)fibdi->FileNameLength;
        FileName.MaximumLength = FileName.Length + 2;

        KdPrint(("FileName %wZ\n", &FileName));  

        if (fibdi->NextEntryOffset == 0)
        {
            break;
        }
    }

    if (FileInformation)
    {
        ExFreePoolWithTag(FileInformation, TAG);
        FileInformation = NULL;
    }

    ZwClose(FileHandle);

    return status;
}


NTSTATUS ZwEnumerateFile2(IN UNICODE_STRING * directory)// ,FILE_INFORMATION_CLASS fic
    /*
    功能:枚举目录下的文件。没有递归。

    输入参数:可以是路径,句柄,文件对象,这个可以修改,但不能是文件路径。

    路径的格式最好是:L"\\??\\C:\\Windows");或者"\Device\HarddiskVolume1\XXX  \\DosDevices\\C:\\,不然还得转换,结构最好用的UNICODE_STRING,这样安全。

    这个是改进版。
    因为上面的函数,不可能确定目录下有多少目录,如系统目录下有几千个文件。
    这个办法是一个一个来的,参考了KMDKIT的第十一章教程。
    */
{
    NTSTATUS status = STATUS_UNSUCCESSFUL;
    OBJECT_ATTRIBUTES ob;
    HANDLE FileHandle = 0;
    IO_STATUS_BLOCK  IoStatusBlock = {0};
    PVOID FileInformation = 0;
    ULONG Length = sizeof (FILE_DIRECTORY_INFORMATION);//这个数设置的太小会导致ZwQueryDirectoryFile蓝屏。  

    InitializeObjectAttributes(&ob, directory, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, 0, 0);
    status = ZwOpenFile(&FileHandle, GENERIC_READ | SYNCHRONIZE, &ob, &IoStatusBlock, FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_NONALERT | FILE_DIRECTORY_FILE);
    if (!NT_SUCCESS (status))
    {
        KdPrint(("ZwOpenFile fail with 0x%x.\n", status));
        if ( status == STATUS_OBJECT_NAME_NOT_FOUND || IoStatusBlock.Information == FILE_DOES_NOT_EXIST)  {
            KdPrint(("file does not exist\n"));
        }
        return status;
    }

    Length = Length + 520;//为何加这个数字,请看ZwEnumerateFile1的说明。
    FileInformation = ExAllocatePoolWithTag(NonPagedPool, Length, TAG);
    if (FileInformation == NULL) {
        status = STATUS_UNSUCCESSFUL;
        DbgPrint("发生错误的文件为:%s, 代码行为:%d\n", __FILE__, __LINE__);
        ZwClose(FileHandle);
        return status;
    }
    RtlZeroMemory(FileInformation, Length);

    status = ZwQueryDirectoryFile(FileHandle, NULL, NULL, NULL, &IoStatusBlock, FileInformation, Length, FileDirectoryInformation, TRUE, NULL, TRUE);
    if (!NT_SUCCESS (status))
    {
        KdPrint(("ZwQueryDirectoryFile fail with 0x%x.\n", status));//STATUS_BUFFER_TOO_SMALL == C0000023
        ExFreePoolWithTag(FileInformation, TAG);
        ZwClose(FileHandle);
        return status;
    }

    do
    {
        UNICODE_STRING FileName ={0};
        FILE_DIRECTORY_INFORMATION * fibdi = 0;

        status = ZwQueryDirectoryFile(FileHandle, NULL, NULL, NULL, &IoStatusBlock, FileInformation, Length, FileDirectoryInformation, TRUE, NULL, FALSE);
        if (status != STATUS_NO_MORE_FILES && status != STATUS_SUCCESS)
        {
            break;//这里好像没有走过。
        }

        fibdi = FileInformation;

        if (FILE_ATTRIBUTE_DIRECTORY == fibdi->FileAttributes)
        {
            //这里可以考虑递归。这里放弃了文件夹的显示。
            continue;
        }

        FileName.Buffer = fibdi->FileName;
        FileName.Length = (USHORT)fibdi->FileNameLength;
        FileName.MaximumLength = FileName.Length + 2;

        KdPrint(("FileName %wZ\n", &FileName));  

    } while (status != STATUS_NO_MORE_FILES);

    if (FileInformation)
    {
        ExFreePoolWithTag(FileInformation, TAG);
        FileInformation = NULL;
    }

    ZwClose(FileHandle);

    return status;
}


#pragma INITCODE
DRIVER_INITIALIZE DriverEntry;
NTSTATUS DriverEntry(__in struct _DRIVER_OBJECT * DriverObject, __in PUNICODE_STRING RegistryPath)
{
    //经测试,这三种类型的路径都是可以的。
    UNICODE_STRING directory  = RTL_CONSTANT_STRING(L"\\??\\C:\\test");
    //UNICODE_STRING directory  = RTL_CONSTANT_STRING(L"\\DosDevices\\C:\\test");
    //UNICODE_STRING directory  = RTL_CONSTANT_STRING(L"\\Device\\HarddiskVolume1\\test");
    /*
    注意:
    \\Device\\HarddiskVolume1\\是可以的;
    \\Device\\HarddiskVolume1是不可以的。
    */

    KdBreakPoint();

    DriverObject->DriverUnload = Unload;

    return ZwEnumerateFile2(&directory);//, FileIdBothDirectoryInformation
}

没有评论:

发表评论