2013年11月23日星期六

驱动复制文件

/*
名字:驱动复制文件.

文件操作很简单.
NT*系列函数的操作在用户层也可以.

为何有今天的文章?
文件操作真的简单吗?看完本篇就知道了.
为何要做ZW*系列函数?在内核中没有权限的限制,不知是哪本书或者那个地方这样说的.
所以在内核就可以做一些应用层难做的事情,如需要特特特特权限的.

网上没有看到相关的示例代码.
只有WDK上的一些简单的介绍:Using Files In A Driver
http://msdn.microsoft.com/en-us/library/windows/hardware/ff565384(v=vs.85).aspx

前两个函数测试如下用例失败:
打开:windows\\system32\\config\\sam
会导致:C0000043 == -1073741757,解释是:另一个程序正在使用此文件,进程无法访问。
可能是独占方式打开的.

made by correy
made at 2013.11.23
homepage:http://correy.webs.com
不足之处,敬请指出.
*/

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

#define TAG 'tset' //本驱动在内存中的标志:test


BOOLEAN copy_file(IN PWCH DestinationFile, IN PWCH SourceFile, IN BOOLEAN bFailIfExists)
    /*
    参数的形式为:C:\WINDOWS\example.txt

    bFailIfExists == TRUE时,如果DestinationFile存在就返回失败;
    bFailIfExists == FALSE时,如果DestinationFile存在就新建或者覆盖;

    存在的缺点有:
    1.没有复制文件的属性,如:文件的所有者等信息.
    */
{
    BOOLEAN b = FALSE;
    NTSTATUS status = STATUS_UNSUCCESSFUL;
    UNICODE_STRING df = {0};
    UNICODE_STRING f  = RTL_CONSTANT_STRING(L"\\??\\");//On Microsoft Windows 2000 and later versions of the operating system, \?? is equivalent to \DosDevices.
    OBJECT_ATTRIBUTES ob;
    HANDLE FileHandle = 0;
    HANDLE DestinationFileHandle = 0;
    IO_STATUS_BLOCK  IoStatusBlock = {0};
    PVOID Buffer = 0;
    ULONG Length = 0;
    LARGE_INTEGER file_size = {0};
    LARGE_INTEGER ByteOffset = {0};
    LARGE_INTEGER i = {0};
    FILE_STANDARD_INFORMATION fsi = {0};
    LARGE_INTEGER AllocationSize = {0};
    ULONG CreateDisposition = 0;

    //如果SourceFile是文件夹,在下面打开的时候返回失败.

    __try 
    { 
        df.Buffer = ExAllocatePoolWithTag(NonPagedPool, MAX_PATH, TAG);
        if (df.Buffer == NULL) { 
            DbgPrint("发生错误的文件为:%s, 代码行为:%d\n", __FILE__, __LINE__);
            __leave;
        }
        RtlZeroMemory(df.Buffer, MAX_PATH);
        RtlInitEmptyUnicodeString(&df,df.Buffer,MAX_PATH);
        RtlCopyUnicodeString(&df,&f);
        status = RtlAppendUnicodeToString(&df, SourceFile);
        if (!NT_SUCCESS (status)) {
            KdPrint(("RtlAppendUnicodeToString fail with 0x%x.\n", status));
            __leave;
        }
        InitializeObjectAttributes(&ob, &df, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, 0, 0);
        status = ZwOpenFile(&FileHandle, GENERIC_READ | SYNCHRONIZE, &ob, &IoStatusBlock, FILE_SHARE_READ, FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT);
        if (!NT_SUCCESS (status)) 
        {
            KdPrint(("ZwOpenFile fail with 0x%x.\n", status));
            if ( status == STATUS_OBJECT_NAME_NOT_FOUND) {
                KdPrint(("file does not exist\n"));
            }
            if (IoStatusBlock.Information == FILE_DOES_NOT_EXIST ) {
                KdPrint(("file does not exist\n"));
            }
            __leave;
        }

        RtlZeroMemory(df.Buffer, MAX_PATH);
        RtlInitEmptyUnicodeString(&df,df.Buffer,MAX_PATH);
        RtlCopyUnicodeString(&df,&f);
        status = RtlAppendUnicodeToString(&df, DestinationFile);
        if (!NT_SUCCESS (status)) {
            KdPrint(("RtlAppendUnicodeToString fail with 0x%x.\n", status));
            __leave;
        }
        InitializeObjectAttributes(&ob, &df, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, 0, 0);        
        if (bFailIfExists) {
            CreateDisposition = FILE_CREATE;//新建文件.
        } else {
            CreateDisposition = FILE_SUPERSEDE;//FILE_OVERWRITE_IF
        }
        status = ZwCreateFile(&DestinationFileHandle, FILE_ALL_ACCESS | SYNCHRONIZE, &ob, &IoStatusBlock, &AllocationSize, 
            FILE_ATTRIBUTE_NORMAL, FILE_SHARE_WRITE, CreateDisposition, FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0);
        if (!NT_SUCCESS (status)) 
        {
            KdPrint(("ZwCreateFile fail with 0x%x.\n", status));
            __leave;
        }

        //可以考虑在这里给文件加锁,保护,不让别的操作再写入.ZwLockFile,再在适当的时候解锁:ZwUnlockFile.

        status = ZwQueryInformationFile(FileHandle, &IoStatusBlock, &fsi, sizeof (FILE_STANDARD_INFORMATION), FileStandardInformation);
        if (!NT_SUCCESS (status)) 
        {
            KdPrint(("ZwQueryInformationFile fail with 0x%x.\n", status));
            __leave;
        }

        if (fsi.EndOfFile.QuadPart == 0)
        {
            __leave;
        }

        file_size = fsi.EndOfFile;
        Length = 9;//可用ZwQuerySystemInformation SystemBasicInformation取页的大小。
        Buffer = ExAllocatePoolWithTag(NonPagedPool, Length, TAG);
        if (Buffer == NULL) { 
            status = STATUS_UNSUCCESSFUL;
            DbgPrint("发生错误的文件为:%s, 代码行为:%d\n", __FILE__, __LINE__);
            __leave;
        }

        for ( ;i.QuadPart < file_size.QuadPart ; )
        {
            RtlZeroMemory(Buffer, Length);

            status = ZwReadFile(FileHandle, NULL, NULL, NULL, &IoStatusBlock, Buffer, Length, &i, NULL);
            if (!NT_SUCCESS (status)) 
            {
                KdPrint(("ZwReadFile fail with 0x%x.\n", status));
                __leave;
            }             
            
            status = ZwWriteFile(DestinationFileHandle, NULL, NULL, NULL, &IoStatusBlock, Buffer, IoStatusBlock.Information, &i, NULL);
            if (!NT_SUCCESS (status)) //可以检查写入的数量或者写入的总量。
            {
                KdPrint(("ZwWriteFile fail with 0x%x.\n", status));
                __leave;
            }

            i.QuadPart += IoStatusBlock.Information;//必须在这里,在ZwWriteFile的前面就会出现大小的问题。
        }
    } __finally {
        if (NT_SUCCESS( status )) //检查上面的所有情况.
        {
            b = TRUE;//有时写文件会返回0x103,即重叠 I/O 操作在进行中,也会走到这里.
        }

        //关闭句柄.        
        if (FileHandle)
        {
            status = ZwClose(FileHandle);
            if (!NT_SUCCESS (status)) 
            {
                KdPrint(("ZwClose fail with 0x%x.\n", status));
            }
        }
        if (DestinationFileHandle)
        {
            status = ZwClose(DestinationFileHandle);
            if (!NT_SUCCESS (status)) 
            {
                KdPrint(("ZwClose fail with 0x%x.\n", status));
            }
        }

        //释放内存.
        if (df.Buffer) {
            ExFreePoolWithTag(df.Buffer, TAG);
        }
        if (Buffer) {
            ExFreePoolWithTag(Buffer, TAG);
        }
    }    

    return b;//不做同步是要蓝屏的.
}


BOOLEAN copy_file_ex(IN UNICODE_STRING * FileName, IN UNICODE_STRING * newFileName) //CONST
    /*
    参数的形式是:"\Device\HarddiskVolume1\XXX等。

    存在的缺点有:
    1.没有复制文件的属性,如:文件的所有者等信息.
    */
{
    BOOLEAN b = FALSE;
    NTSTATUS status = STATUS_UNSUCCESSFUL;
    OBJECT_ATTRIBUTES ob;
    HANDLE FileHandle = 0;
    HANDLE DestinationFileHandle = 0;
    IO_STATUS_BLOCK  IoStatusBlock = {0};
    PVOID Buffer = 0;
    ULONG Length = 0;
    ULONG CreateDisposition = 0;
    FILE_STANDARD_INFORMATION fsi = {0};
    LARGE_INTEGER ByteOffset = {0};
    LARGE_INTEGER AllocationSize = {0};
    LARGE_INTEGER file_size = {0};

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

    //可以考虑在这里给文件加锁,保护,不让别的操作再写入.ZwLockFile,再在适当的时候解锁:ZwUnlockFile.

    status = ZwQueryInformationFile(FileHandle, &IoStatusBlock, &fsi, sizeof (FILE_STANDARD_INFORMATION), FileStandardInformation);
    if (!NT_SUCCESS (status)) 
    {
        KdPrint(("ZwQueryInformationFile fail with 0x%x.\n", status));
        ZwClose(FileHandle);
        return b;;
    }

    //新建文件.
    CreateDisposition = FILE_CREATE;
    InitializeObjectAttributes(&ob, newFileName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, 0, 0);
    status = ZwCreateFile(&DestinationFileHandle, FILE_ALL_ACCESS | SYNCHRONIZE, &ob, &IoStatusBlock, &AllocationSize, 
        FILE_ATTRIBUTE_NORMAL, FILE_SHARE_WRITE, CreateDisposition, FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0);
    if (!NT_SUCCESS (status)) 
    {
        //KdPrint(("ZwCreateFile fail with 0x%x.\n", status));
        ZwClose(FileHandle);
        if (status == STATUS_OBJECT_NAME_COLLISION)   {//-1073741771 ((NTSTATUS)0xC0000035L) Object Name already exists.
            b = TRUE;
        }
        return b;
    }
    
    if (fsi.EndOfFile.QuadPart == 0)
    {
        ZwClose(FileHandle);
        ZwClose(DestinationFileHandle);
        return TRUE ;
    }

    file_size = fsi.EndOfFile;
    Length = 9;//测试专用。
    Buffer = ExAllocatePoolWithTag(NonPagedPool, Length, TAG);//Length == 0时加驱动验证器,这里会蓝屏。
    if (Buffer == NULL) { 
        status = STATUS_UNSUCCESSFUL;
        DbgPrint("发生错误的文件为:%s, 代码行为:%d\n", __FILE__, __LINE__);
        ZwClose(FileHandle);
        ZwClose(DestinationFileHandle);
        return b;
    }

    for ( ;ByteOffset.QuadPart < file_size.QuadPart ; )
    {
        RtlZeroMemory(Buffer, Length);

        status = ZwReadFile(FileHandle, NULL, NULL, NULL, &IoStatusBlock, Buffer, Length, &ByteOffset, NULL);
        if (!NT_SUCCESS (status)) 
        {
            KdPrint(("ZwReadFile fail with 0x%x.\n", status));
            ExFreePoolWithTag(Buffer, TAG);
            ZwClose(FileHandle);
            ZwClose(DestinationFileHandle);
            return b;
        }

        status = ZwWriteFile(DestinationFileHandle, NULL, NULL, NULL, &IoStatusBlock, Buffer, IoStatusBlock.Information, &ByteOffset, NULL);
        if (!NT_SUCCESS (status)) 
        {
            KdPrint(("ZwWriteFile fail with 0x%x.\n", status));
            ExFreePoolWithTag(Buffer, TAG);
            ZwClose(FileHandle);
            ZwClose(DestinationFileHandle);
            return b;
        }

        ByteOffset.QuadPart += IoStatusBlock.Information;
    }

    ExFreePoolWithTag(Buffer, TAG);
    ZwClose(FileHandle);
    ZwClose(DestinationFileHandle);

    return TRUE ;
}


BOOLEAN copy_file_ex2(IN UNICODE_STRING * FileName, IN UNICODE_STRING * newFileName) //CONST
    /*
    参数的形式是:"\Device\HarddiskVolume1\XXX或者\\??\\c:\\WINDOWS\\system32\\config\\SAM。

    功能:由于专门复制被独占式使用的文件,如分页文件(正在使用的pagefile.sys)和各种被正在使用HIVE文件.
    扩展功能:如删除文件(打开的时候带有删除的属性:FILE_DELETE_ON_CLOSE )估计也可以的,这个不用发送IRP,至少在形式上。

    说明:IoCreateFileEx函数有IO_IGNORE_SHARE_ACCESS_CHECK功能,可是This routine is available starting with Windows Vista.

    存在的缺点有:
    1.没有复制文件的属性,如:文件的所有者等信息.

    made by correy
    made at 2014.07.28
    homepage:http://correy.webs.com
    */
{
    BOOLEAN b = FALSE;
    NTSTATUS status = STATUS_UNSUCCESSFUL;
    OBJECT_ATTRIBUTES ob;
    HANDLE FileHandle = 0;
    HANDLE DestinationFileHandle = 0;
    IO_STATUS_BLOCK  IoStatusBlock = {0};
    PVOID Buffer = 0;
    ULONG Length = 0;
    ULONG CreateDisposition = 0;
    FILE_STANDARD_INFORMATION fsi = {0};
    LARGE_INTEGER ByteOffset = {0};
    LARGE_INTEGER AllocationSize = {0};
    LARGE_INTEGER file_size = {0};
    FILE_FULL_EA_INFORMATION ffai = {0};

    InitializeObjectAttributes(&ob, FileName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, 0, 0);
    //status = ZwOpenFile(&FileHandle, GENERIC_READ | SYNCHRONIZE, &ob, &IoStatusBlock, FILE_SHARE_READ, FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT);
    //if (!NT_SUCCESS (status)) 
    //{
    //    //KdPrint(("ZwOpenFile fail with 0x%x.\n", status));
    //    if ( status == STATUS_OBJECT_NAME_NOT_FOUND)  {
    //        KdPrint(("file does not exist\n"));
    //    }
    //    if (IoStatusBlock.Information == FILE_DOES_NOT_EXIST ) {
    //        KdPrint(("file does not exist\n"));
    //    }
    //    return b;
    //}
    status = IoCreateFileSpecifyDeviceObjectHint(&FileHandle, GENERIC_READ | SYNCHRONIZE, &ob, &IoStatusBlock, &AllocationSize, FILE_ATTRIBUTE_NORMAL, 
        /*
        Specifies the type of share access to the file that the caller would like, as zero, or one, or a combination of the following flags. 
        To request exclusive access, set this parameter to zero. 
        If the IO_IGNORE_SHARE_ACCESS_CHECK flag is specified in the Options parameter, the I/O manager ignores this parameter. 
        However, the file system might still perform access checks.
        Thus, it is important to specify the sharing mode you would like for this parameter, even when using the IO_IGNORE_SHARE_ACCESS_CHECK flag.
        For the greatest chance of avoiding sharing violation errors, specify all of the following share access flags. 
        */
        FILE_SHARE_VALID_FLAGS, 
        FILE_OPEN,
        FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
        &ffai,
        sizeof (FILE_FULL_EA_INFORMATION),
        CreateFileTypeNone,//其实命名管道和邮件槽也定义了。
        NULL,
        /*
        Indicates that the I/O manager should not perform share-access checks on the file object after it is created. 
        However, the file system might still perform these checks. 
        */
        IO_IGNORE_SHARE_ACCESS_CHECK,
        /*
        A pointer to the device object to which the create request is to be sent. 
        The device object must be a filter or file system device object in the file system driver stack for the volume on which the file or directory resides. 
        This parameter is optional and can be NULL. If this parameter is NULL, the request will be sent to the device object at the top of the driver stack. 
        */
        NULL
        );
    if (!NT_SUCCESS (status)) 
    {
        //KdPrint(("ZwOpenFile fail with 0x%x.\n", status));
        if ( status == STATUS_OBJECT_NAME_NOT_FOUND)  {
            KdPrint(("file does not exist\n"));
        }
        if (IoStatusBlock.Information == FILE_DOES_NOT_EXIST ) {
            KdPrint(("file does not exist\n"));
        }
        return b;
    }

    //可以考虑在这里给文件加锁,保护,不让别的操作再写入.ZwLockFile,再在适当的时候解锁:ZwUnlockFile.
    //可是This routine is available in Windows 7 and later versions of the Windows operating system.

    status = ZwQueryInformationFile(FileHandle, &IoStatusBlock, &fsi, sizeof (FILE_STANDARD_INFORMATION), FileStandardInformation);
    if (!NT_SUCCESS (status)) 
    {
        KdPrint(("ZwQueryInformationFile fail with 0x%x.\n", status));
        ZwClose(FileHandle);
        return b;;
    }

    //新建文件.
    CreateDisposition = FILE_CREATE;
    InitializeObjectAttributes(&ob, newFileName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, 0, 0);
    status = ZwCreateFile(&DestinationFileHandle, FILE_ALL_ACCESS | SYNCHRONIZE, &ob, &IoStatusBlock, &AllocationSize, 
        FILE_ATTRIBUTE_NORMAL, FILE_SHARE_WRITE, CreateDisposition, FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0);
    if (!NT_SUCCESS (status)) 
    {
        //KdPrint(("ZwCreateFile fail with 0x%x.\n", status));
        ZwClose(FileHandle);
        if (status == STATUS_OBJECT_NAME_COLLISION)   {//-1073741771 ((NTSTATUS)0xC0000035L) Object Name already exists.
            b = TRUE;
        }
        return b;
    }
    
    if (fsi.EndOfFile.QuadPart == 0)
    {
        ZwClose(FileHandle);
        ZwClose(DestinationFileHandle);
        return TRUE ;
    }

    file_size = fsi.EndOfFile;
    Length = 9;//测试专用。
    Buffer = ExAllocatePoolWithTag(NonPagedPool, Length, TAG);//Length == 0时加驱动验证器,这里会蓝屏。
    if (Buffer == NULL) { 
        status = STATUS_UNSUCCESSFUL;
        DbgPrint("发生错误的文件为:%s, 代码行为:%d\n", __FILE__, __LINE__);
        ZwClose(FileHandle);
        ZwClose(DestinationFileHandle);
        return b;
    }

    for ( ;ByteOffset.QuadPart < file_size.QuadPart ; )
    {
        RtlZeroMemory(Buffer, Length);

        status = ZwReadFile(FileHandle, NULL, NULL, NULL, &IoStatusBlock, Buffer, Length, &ByteOffset, NULL);
        if (!NT_SUCCESS (status)) 
        {
            KdPrint(("ZwReadFile fail with 0x%x.\n", status));
            ExFreePoolWithTag(Buffer, TAG);
            ZwClose(FileHandle);
            ZwClose(DestinationFileHandle);
            return b;
        }

        status = ZwWriteFile(DestinationFileHandle, NULL, NULL, NULL, &IoStatusBlock, Buffer, IoStatusBlock.Information, &ByteOffset, NULL);
        if (!NT_SUCCESS (status)) 
        {
            KdPrint(("ZwWriteFile fail with 0x%x.\n", status));
            ExFreePoolWithTag(Buffer, TAG);
            ZwClose(FileHandle);
            ZwClose(DestinationFileHandle);
            return b;
        }

        ByteOffset.QuadPart += IoStatusBlock.Information;
    }

    ExFreePoolWithTag(Buffer, TAG);
    ZwClose(FileHandle);
    ZwClose(DestinationFileHandle);

    return TRUE ;
}


DRIVER_UNLOAD DriverUnload;
VOID DriverUnload(__in PDRIVER_OBJECT DriverObject)
{   

}


#pragma INITCODE
DRIVER_INITIALIZE DriverEntry;
NTSTATUS DriverEntry(__in struct _DRIVER_OBJECT * DriverObject, __in PUNICODE_STRING RegistryPath)
{
    UNICODE_STRING FileName  = RTL_CONSTANT_STRING(L"\\??\\c:\\1.exe");
    UNICODE_STRING newFileName  = RTL_CONSTANT_STRING(L"\\??\\c:\\3.exe");
    UNICODE_STRING FileName2  = RTL_CONSTANT_STRING(L"\\??\\c:\\WINDOWS\\system32\\config\\SAM");
    UNICODE_STRING newFileName2  = RTL_CONSTANT_STRING(L"\\??\\c:\\WINDOWS\\system32\\config\\SAM2");
    UNICODE_STRING FileName3  = RTL_CONSTANT_STRING(L"\\Device\\HarddiskVolume1\\WINDOWS\\system32\\config\\SAM");//
    UNICODE_STRING newFileName3  = RTL_CONSTANT_STRING(L"\\Device\\HarddiskVolume1\\WINDOWS\\system32\\config\\SAM3");

    KdBreakPoint();

    DriverObject->DriverUnload = DriverUnload;

    if (!copy_file(L"c:\\1.exe", L"c:\\2.exe", FALSE))
    {
        KdPrint(("copy_file fail!\n"));
    }

    if (!copy_file_ex(&FileName, &newFileName))
    {
        KdPrint(("copy_file_ex fail!\n"));
    }

    if (!copy_file_ex2(&FileName2, &newFileName2))
    {
        KdPrint(("copy_file_ex2 fail!\n"));
    }

    if (!copy_file_ex2(&FileName3, &newFileName3))
    {
        KdPrint(("copy_file_ex2 fail!\n"));
    }

    return STATUS_SUCCESS;
} 

没有评论:

发表评论