2013年12月23日星期一

Windows自带的解压缩

/*
压缩是一个高深的算法,可以是是计算机科学.
令我等文盲望而止步.

干编程且可不知压缩,大多数人都是用的开源的算法(库).

这万不得已而用之,其实我也不想用.

知道WIN 8有压缩的函数.

前些时断发现XP也有,只是一COM的形式提供.

不错,就是不知它啥时候把活干完,
这是改进之处.

函数还有待改进,如返回的类型,错误的判断,但是实验是正确的可用的.

made by correy
made at 2013.12.23
email:kouleguan at hotmail dot com
homepage:http://correy.webs.com
*/

#include <ShlDisp.h>

#include <shlwapi.h>
#pragma comment(lib, "shlwapi.lib")

void zip(BSTR source,BSTR dest)
    /*
    source可以是文件也可以是文件夹.
    dest的后缀名必须是ZIP.
    */
{
    //确保source目录存在.
    if (!PathFileExists(source))
    {
        return;
    }

// Create Zip file
BYTE startBuffer[] = {80, 75, 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
    HANDLE hFile = CreateFile(dest, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);//L"C:\\test.zip"
    if (hFile == INVALID_HANDLE_VALUE)  {
        printf("Could not open file (error %d)\n", GetLastError());
        return;
    }
    DWORD dwResult;
    if(!WriteFile (hFile, startBuffer, sizeof(startBuffer), &dwResult, NULL))
    {
        printf("Could not write to file (error %d)\n", GetLastError());
        return;
    }
    if (!CloseHandle(hFile))
    {
        printf("Could not close to file (error %d)\n", GetLastError());
        return;
    }

    CoInitialize(NULL);

    IShellDispatch * pISD;
    HRESULT hResult = CoCreateInstance(CLSID_Shell, NULL, CLSCTX_INPROC_SERVER, IID_IShellDispatch, (void **)&pISD);
    if  (SUCCEEDED(hResult))
    {
        VARIANT          vDir;
        VariantInit(&vDir);
        vDir.vt = VT_BSTR;
        vDir.bstrVal = dest;//L"C:\\test.zip\\\0\0";
       
        Folder * pToFolder = NULL;
        hResult = pISD->NameSpace(vDir, &pToFolder);
        if  (SUCCEEDED(hResult))
        {
            VARIANT vFile, vOpt;

            VariantInit(&vFile);
            vFile.vt = VT_BSTR;
            vFile.bstrVal = source;//L"C:\\test.txt";
           
VariantInit(&vOpt);
            vOpt.vt = VT_I4;
            vOpt.lVal = FOF_NO_UI;//4;          // Do not display a progress dialog box, not useful in our example

            // Now copy source file(s) to the zip
            // ******NOTE**** To copy multiple files into the zip, need to create a FolderItems object (see unzip implementation below for more details)
hResult = pToFolder->CopyHere(vFile, vOpt);// Copying and compressing the source files to our zip

/* CopyHere() creates a separate thread to copy files and it may happen that the main thread exits before the copy thread is initialized.
            So we put the main thread to sleep for a second to give time for the copy thread to start.*/
Sleep(1000);
            pToFolder->Release();
        }
        pISD->Release();
    }
    CoUninitialize();
}

void unzip(BSTR source,BSTR dest)
    /*
    把一个ZIP压缩文件解压到指定的文件夹.
    */
{
    //确保dest目录存在.
    if (!PathFileExists(dest))
    {
        return;
    }

    CoInitialize(NULL);

    IShellDispatch * pISD;
    HRESULT hResult = CoCreateInstance(CLSID_Shell, NULL, CLSCTX_INPROC_SERVER, IID_IShellDispatch, (void **)&pISD);
    if  (SUCCEEDED(hResult))
    {
        VARIANT vDir;
        VariantInit(&vDir);
        vDir.vt = VT_BSTR;
        vDir.bstrVal = dest;//L"C:\\test.zip\\\0\0";

        Folder * pToFolder = NULL;
        hResult = pISD->NameSpace(vDir, &pToFolder);
        if  (SUCCEEDED(hResult))
        {
            VARIANT vFile;
VariantInit(&vFile);
            vFile.vt = VT_BSTR;
            vFile.bstrVal = source;//L"C:\\test.txt";

            Folder * pFromFolder = NULL;
pISD->NameSpace(vFile, &pFromFolder);

FolderItems * fi = NULL;
pFromFolder->Items(&fi);

            VARIANT vOpt;
            VariantInit(&vOpt);
            vOpt.vt = VT_I4;
            vOpt.lVal = FOF_NO_UI;//4; // Do not display a progress dialog box

// Creating a new Variant with pointer to FolderItems to be copied
VARIANT newV;
VariantInit(&newV);
newV.vt = VT_DISPATCH;
newV.pdispVal = fi;

hResult = pToFolder->CopyHere(newV, vOpt);

Sleep(1000);
pFromFolder->Release();
            pToFolder->Release();
        }
        pISD->Release();
    }
    CoUninitialize();
}

int main()
{
    zip(L"d:\\test1", L"d:\\test.zip");//\0\0 \\\0\0
unzip(L"d:\\test.zip", L"d:\\test2");//\\\0\0  \\\0\0
    return 0;
}

2013年12月13日星期五

win8的特殊启动方式

本文是关于win8的特殊启动方式.

最简单的有效的方式是:运行->msconfig.
不过这个只能是安全模式,调试模式等.
特别说明:在WIN 8 X64下,虽然成功的以调试模式启动了,但必须处于调试模式下(也就是连接到了调试器),才能加载驱动.这是个人经验.

禁用签名的办法有:
1.命令行:
  bcdedit -set loadoptions DDISABLE_INTEGRITY_CHECKS
  bcdedit -set TESTSIGNING ON
  需要管理员的权限.
  恢复的命令为:
  bcdedit -set loadoptions ENABLE_INTEGRITY_CHECKS
  bcdedit -set TESTSIGNING OFF
2.Windows8系统下:
  键盘按键[Win]+[C]调出系统的超级按钮Charm菜单->设置->更改电脑设置->常规->高级启动(立即重启)->疑难解答->高级选项->启动设置->重新启动,
  然后等系统自己加载,就可以进入高级启动菜单界面了,看到“禁用驱动程序强制签名”,选择它就行。算是成功禁用了。
3.Windows8.1系统操作有些不同:
  盘按键[Win]+[C]调出系统的超级按钮Charm菜单->设置->更改电脑设置->更新和恢复->恢复->高级启动(立即重启)->操作都一样了~
  这个内容摘自:http://www.wenzige.com/post/win8-disable-enable_integrity_checks.html.

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;
} 

2013年11月19日星期二

BUGCHECK:0xc4_e3的通解

BUGCHECK:0xc4_e3的通解.

原因:是在和函数访问了用户的数据,解决办法吗?最简单的就是copy.

关键是看:访问了用户的哪个地址和内核的哪个函数的哪个参数用到了那个用户的地址.

1: kd> !analyze -v
*******************************************************************************
*                                                                             *
*                        Bugcheck Analysis                                    *
*                                                                             *
*******************************************************************************

DRIVER_VERIFIER_DETECTED_VIOLATION (c4)
A device driver attempting to corrupt the system has been caught.  This is
because the driver was specified in the registry as being suspect (by the
administrator) and the kernel has enabled substantial checking of this driver.
If the driver attempts to corrupt the system, bugchecks 0xC4, 0xC1 and 0xA will
be among the most commonly seen crashes.
Arguments:
Arg1: 00000000000000e3, Kernel Zw API called with user-mode address as parameter.
Arg2: fffff880060cacab, Address inside the driver making the incorrect API call.
Arg3: 000007fefbdccb40, User-mode address used as API parameter.
Arg4: 0000000000000000

Debugging Details:
------------------


BUGCHECK_STR:  0xc4_e3

FAULTING_IP:
rdrct64!Process_CreateKey+18b [e:\securedesktop\dpdesk\rdrct\regeditvista.c @ 311]
fffff880`060cacab 89442444        mov     dword ptr [rsp+44h],eax

FOLLOWUP_IP:
rdrct64!Process_CreateKey+18b [e:\securedesktop\dpdesk\rdrct\regeditvista.c @ 311]
fffff880`060cacab 89442444        mov     dword ptr [rsp+44h],eax

DEFAULT_BUCKET_ID:  WIN7_DRIVER_FAULT

PROCESS_NAME:  explorer.exe

CURRENT_IRQL:  2

LAST_CONTROL_TRANSFER:  from fffff800019b9a02 to fffff800018c09b0

STACK_TEXT:
fffff880`06670a88 fffff800`019b9a02 : 00000000`000000e3 fffffa80`05ee0720 00000000`00000065 fffff800`01903898 : nt!RtlpBreakWithStatusInstruction
fffff880`06670a90 fffff800`019ba7ee : 000007fe`00000003 00000000`00000000 fffff800`019040f0 00000000`000000c4 : nt!KiBugCheckDebugBreak+0x12
fffff880`06670af0 fffff800`018c8c84 : fffff980`08deac00 fffff800`01d5e012 00000000`00000400 fffff800`01d5832e : nt!KeBugCheck2+0x71e
fffff880`066711c0 fffff800`01d564ec : 00000000`000000c4 00000000`000000e3 fffff880`060cacab 000007fe`fbdccb40 : nt!KeBugCheckEx+0x104
fffff880`06671200 fffff800`01d56fd5 : 00000000`00000000 fffff800`01d584ae 00000000`00000000 fffff800`01893081 : nt!VerifierBugCheckIfAppropriate+0x3c
fffff880`06671240 fffff800`01d584ae : fffff880`060cacab fffff800`01d5854e fffff880`06671390 fffff880`060cacab : nt!ViZwCheckAddress+0x35
fffff880`06671280 fffff800`01d5cc28 : fffff880`06671390 fffff880`060cacab 00000000`00ee00ec fffff980`04186c00 : nt!ViZwCheckUnicodeString+0x2e
fffff880`066712c0 fffff880`060cacab : 00000000`00000000 fffff880`06671a50 00000000`00000000 fffff880`06671380 : nt!VfZwCreateKey+0x68
fffff880`06671320 fffff880`060cb451 : fffff880`06671680 fffff880`06671680 fffff8a0`04217570 fffff800`01c741d1 : rdrct64!Process_CreateKey+0x18b [e:\securedesktop\dpdesk\rdrct\regeditvista.c @ 311]
fffff880`066713d0 fffff800`01c74460 : 00000000`00000000 00000000`0000001a fffff880`06671680 00000000`000007ff : rdrct64!RegistryCallback+0x91 [e:\securedesktop\dpdesk\rdrct\regeditvista.c @ 1179]
fffff880`06671420 fffff800`01b2bedd : fffffa80`0000001a fffff880`06671680 fffffa80`05ee0701 fffff880`0000001b : nt!CmpCallCallBacks+0x1c0
fffff880`066714f0 fffff800`01bc56d8 : fffffa80`0590acc8 fffffa80`00000000 fffffa80`0590ab10 fffff8a0`00000001 : nt! ?? ::NNGAKEGL::`string'+0x2ad6d
fffff880`066717f0 fffff800`01bc68f6 : 00000000`000000d4 fffffa80`0590ab10 16a00000`01b7d860 fffffa80`03d36f30 : nt!ObpLookupObjectName+0x588
fffff880`066718e0 fffff800`01b7d024 : fffff880`06671b88 00000000`00000000 fffff880`06671a01 fffff800`018c6dbd : nt!ObOpenObjectByName+0x306
fffff880`066719b0 fffff800`01b7d8c2 : 00000000`053fe3b8 fffff880`0002001f 00000000`053fe3d8 fffff800`01b7d894 : nt!CmCreateKey+0x2e1
fffff880`06671b20 fffff800`018c7e13 : fffffa80`05ee0720 0000007f`00000007 00000000`053fe318 00000980`00000000 : nt!NtCreateKey+0x2e
fffff880`06671b70 00000000`775f148a : 00000000`7738d4d8 00000000`053fe7a8 00000000`00000000 00000000`00000000 : nt!KiSystemServiceCopyEnd+0x13
00000000`053fe368 00000000`7738d4d8 : 00000000`053fe7a8 00000000`00000000 00000000`00000000 000007fe`fbe6626a : ntdll!NtCreateKey+0xa
00000000`053fe370 00000000`7738d7c8 : 00000000`053fe7a8 00000000`053fe780 00000000`00000000 00000000`053fe8d8 : kernel32!Wow64NtCreateKey+0xe8
00000000`053fe440 00000000`7738d343 : 000007fe`fe9053c0 00000000`00000170 00000000`053fe8d8 00000000`053fe788 : kernel32!LocalBaseRegCreateKey+0x282
00000000`053fe720 00000000`7738d3e0 : 00000000`000000d4 00000000`00000000 00000000`0399c020 00000000`00000000 : kernel32!RegCreateKeyExInternalW+0x15c
00000000`053fe800 000007fe`fbddfe97 : 00000000`0399c020 00000000`0001035a 00000000`00000190 00000000`00000000 : kernel32!RegCreateKeyExW+0x50
00000000`053fe860 000007fe`fe5ad3fc : 00000000`053fe990 00000000`00030352 00000000`053fe978 00000000`053fe970 : comctl32!CreateMRUListLazyW+0x8f
00000000`053fe940 000007fe`fe55b5b2 : 00000000`00000004 00000000`774ba85d 00000000`0001035a 00000000`00000141 : SHELL32!CreateMRUListW+0x38
00000000`053fe970 000007fe`fe55a0ae : 00000000`00000000 00000000`0399c020 00000000`0399c020 00000000`0399c020 : SHELL32!OpenRunDlgMRU+0x5e
00000000`053fe9d0 000007fe`fe559f63 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000087 : SHELL32!CRunDlg::InitRunDlg+0x185
00000000`053fec70 00000000`774ae53b : 00000000`038b4830 00000000`00000000 00000000`00000001 ffffffff`fc74b7cf : SHELL32!RunDlgProc+0x279
00000000`053fed20 00000000`774ae2f2 : 00000000`00000110 000007fe`fe55af1c 00000000`00700c80 00000000`0001035a : USER32!GetCapture+0x40b
00000000`053fede0 00000000`774d4843 : 00000000`00000000 00000000`00700c80 00000000`00000000 00000000`00030352 : USER32!GetCapture+0x1c2
00000000`053fee60 00000000`00000000 : 00000000`00700c80 00000000`00000000 00000000`00030352 00000000`00000000 : USER32!UserHandleGrantAccess+0x8d73


STACK_COMMAND:  kb

FAULTING_SOURCE_LINE:  e:\securedesktop\dpdesk\rdrct\regeditvista.c

FAULTING_SOURCE_FILE:  e:\securedesktop\dpdesk\rdrct\regeditvista.c

FAULTING_SOURCE_LINE_NUMBER:  311

SYMBOL_STACK_INDEX:  8

SYMBOL_NAME:  rdrct64!Process_CreateKey+18b

FOLLOWUP_NAME:  MachineOwner

MODULE_NAME: rdrct64

IMAGE_NAME:  rdrct64.sys

DEBUG_FLR_IMAGE_TIMESTAMP:  5285ddfd

FAILURE_BUCKET_ID:  X64_0xc4_e3_VRF_rdrct64!Process_CreateKey+18b

BUCKET_ID:  X64_0xc4_e3_VRF_rdrct64!Process_CreateKey+18b

Followup: MachineOwner
---------

关键的问题在这里:
nt!ViZwCheckAddress:
                           fffff800`01d0efa0 4883ec38        sub     rsp,38h
                           fffff800`01d0efa4 4885c9          test    rcx,rcx
                           fffff800`01d0efa7 742c            je      nt!ViZwCheckAddress+0x35 (fffff800`01d0efd5)
                           fffff800`01d0efa9 483b0d6090daff  cmp     rcx,qword ptr [nt!MmHighestUserAddress (fffff800`01ab8010)]
                           fffff800`01d0efb0 7323            jae     nt!ViZwCheckAddress+0x35 (fffff800`01d0efd5)
                           fffff800`01d0efb2 8b0528f10400    mov     eax,dword ptr [nt!ViZwBreakForIssues (fffff800`01d5e0e0)]
                           fffff800`01d0efb8 85c0            test    eax,eax
                           fffff800`01d0efba 7419            je      nt!ViZwCheckAddress+0x35 (fffff800`01d0efd5)
                           fffff800`01d0efbc 488364242000    and     qword ptr [rsp+20h],0
                           fffff800`01d0efc2 4c8bc2          mov     r8,rdx
                           fffff800`01d0efc5 bae3000000      mov     edx,0E3h
                           fffff800`01d0efca 4c8bc9          mov     r9,rcx
                           fffff800`01d0efcd 8d4ae1          lea     ecx,[rdx-1Fh]
                           fffff800`01d0efd0 e8dbf4ffff      call    nt!VerifierBugCheckIfAppropriate (fffff800`01d0e4b0)
                           fffff800`01d0efd5 4883c438        add     rsp,38h
                           fffff800`01d0efd9 c3              ret

这是用异常处理也处理不了的.

made by correy
made at 2013.11.19

2013年11月18日星期一

枚举桌面的内容

/*
文件名:IShellFolder.Cpp
功能:列出桌面的所以的(子)文件和(子)文件夹.

至今方知目录和文件夹的区别.
Windows Shell一直是一个知道但不熟悉的名字.

Windows Shell搞懂了,再搞COM等.

参考示例:
http://msdn.microsoft.com/en-us/library/windows/desktop/bb776885(v=vs.85).aspx
http://msdn.microsoft.com/en-us/library/windows/desktop/bb776889(v=vs.85).aspx

made by correy
made at 2013.11.18
email:kouleguan at hotmail dot com
homepage:http://correy.webs.com
不足之处,敬请指出.
*/

#include <shlobj.h>
#pragma comment(lib, "shlwapi.lib")

int main()
{  
    IShellFolder *psfDeskTop = NULL;
    HRESULT hr = SHGetDesktopFolder(&psfDeskTop);//获取对象的地址.

    LPENUMIDLIST ppenum = NULL;    
    hr = psfDeskTop->EnumObjects(NULL,SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, &ppenum);//获取对象的地址.
   
    ULONG celtFetched;
    LPITEMIDLIST pidlItems = NULL;
    while( hr = ppenum->Next(1,&pidlItems, &celtFetched) == S_OK && (celtFetched) == 1) //获取pidlItems
    {
        STRRET strDispName;
        psfDeskTop->GetDisplayNameOf(pidlItems, SHGDN_INFOLDER, &strDispName);//获取strDispName

        MessageBox(0,strDispName.pOleStr,0,0);

        CoTaskMemFree(pidlItems);
    }

    ppenum->Release();

    return 0;
}

2013年11月15日星期五

官方的驱动中获取内核模块列表的办法

/*
文件名:AuxKlibQueryModuleInformation.C
功能:retrieves information about the image modules that the operating system has loaded.

SOURCE文件内容如下:
TARGETNAME=test

TARGETTYPE=DRIVER

TARGETLIBS=$(DDK_LIB_PATH)\Aux_klib.lib

SOURCES=AuxKlibQueryModuleInformation.C

TARGETPATH=obj

made by correy
made at 2013.11.15
email:kouleguan at hotmail dot com
homepage:http://correy.webs.com
不足之处,敬请指出.
*/

#include <ntifs.h>
#include <Aux_klib.h>

#define TAG  'tset' //test

DRIVER_UNLOAD Unload;
VOID Unload(__in PDRIVER_OBJECT DriverObject)
{  

}

#pragma INITCODE
DRIVER_INITIALIZE DriverEntry;
NTSTATUS DriverEntry(__in struct _DRIVER_OBJECT * DriverObject, __in PUNICODE_STRING RegistryPath)
{
    NTSTATUS status = 0;
    ULONG  modulesSize;
    AUX_MODULE_EXTENDED_INFO * modules;
    ULONG  numberOfModules;
    ULONG i;
    PIMAGE_EXPORT_DIRECTORY pied = 0;

    KdBreakPoint();

    DriverObject->DriverUnload = Unload;

    status = AuxKlibInitialize();
    if (!NT_SUCCESS( status ))
    {
        KdPrint(( "AuxKlibInitialize fail %d\n", status));
        return status;
    }  
 
    status = AuxKlibQueryModuleInformation(&modulesSize, sizeof(AUX_MODULE_EXTENDED_INFO), NULL);// Get the required array size.
    if (!NT_SUCCESS(status) || modulesSize == 0) {
        return status;
    }
 
    numberOfModules = modulesSize / sizeof(AUX_MODULE_EXTENDED_INFO);// Calculate the number of modules.
     
    modules = (AUX_MODULE_EXTENDED_INFO*) ExAllocatePoolWithTag(PagedPool, modulesSize, TAG);// Allocate memory to receive data.
    if (modules == NULL) {
        status = STATUS_INSUFFICIENT_RESOURCES;
        return status;
    }
    RtlZeroMemory(modules, modulesSize);
 
    status = AuxKlibQueryModuleInformation(&modulesSize, sizeof(AUX_MODULE_EXTENDED_INFO), modules);// Obtain the module information.
    if (!NT_SUCCESS(status)) {
        ExFreePoolWithTag(modules,TAG);
        return status;
    }

    for (i = 0;i<numberOfModules;i++)
    {
        KdPrint(("加载顺序:%d\n",i));
        KdPrint(("FileName:%s\n",modules[i].FullPathName + modules[i].FileNameOffset));
        KdPrint(("ImageBase:%p\n",modules[i].BasicInfo.ImageBase));
        KdPrint(("ImageSize:0x%08x\n",modules[i].ImageSize));
        KdPrint(("FullPathName:%s\n",modules[i].FullPathName));
        KdPrint(("\n\n"));

        //注意传递的地址不要错,要可以访问的,并且是一个文件内存的地址.
        //PVOID ImageBase = modules[i].BasicInfo.ImageBase;
        //pied = AuxKlibGetImageExportDirectory(ImageBase);
        //这里可以打印每个驱动的更多的信息.
    }

    ExFreePoolWithTag(modules,TAG);
    return status;
}

在驱动中获取进程的用户名及SID

/*
文件名:GetSecurityUserInfo.C

功能:获取当前操作注册表的进程的用户.

SOURCE文件内容如下:
TARGETNAME=test

TARGETTYPE=DRIVER

TARGETLIBS=$(DDK_LIB_PATH)\ksecdd.lib 

SOURCES=GetSecurityUserInfo.C 

TARGETPATH=obj

made by correy
made at 2013.11.15
email:kouleguan at hotmail dot com
homepage:http://correy.webs.com
不足之处,敬请指出.
*/

#include <ntifs.h>

LARGE_INTEGER  Cookie;

NTSTATUS print_current_user()
    /*
    打印当前操作的当前进程的用户名等.
    */
{
    NTSTATUS status = 0;
    PACCESS_TOKEN pat = 0;    
    UNICODE_STRING  SidString = {0};
    PTOKEN_STATISTICS pts;
    PSecurityUserData  PUserInformation;

    pat = PsReferencePrimaryToken(PsGetCurrentProcess());    

    status = SeQueryInformationToken(pat, TokenStatistics,(PVOID *) & pts);
    if( !NT_SUCCESS( status ) )
    {
        PsDereferencePrimaryToken(pat);
        return status;
    }

    //还有一种情况也会发生如下情况,就是在Windows server 2008 X64上,具体的有待检查和添加.
    if (pts->AuthenticationId.HighPart == 0 && pts->AuthenticationId.LowPart == 999)//为何是这两个数,不得而知.
    {
        //获取到了系统帐户,就是system,本地系统帐户.
        //打印的消息一般如下:
        //UserName :KOUDAQIANG-2008$
        //LogonServer :
        //LogonDomainName :WORKGROUP
        //SID :S-1-5-18

        KdPrint(("UserName:system\n"));

        PsDereferencePrimaryToken(pat);
        return status;
    }

    //可以考虑把下面的代码写成工作线程.

    //下面的函数需要连接Ksecdd.lib,如:TARGETLIBS=$(DDK_LIB_PATH)\ksecdd.lib
    status = GetSecurityUserInfo(&pts->AuthenticationId, 0, &PUserInformation);
    if( !NT_SUCCESS( status ) )
    {
        return status;
    }

    //status = RtlConvertSidToUnicodeString(&SidString, PUserInformation->pSid,TRUE);
    //if( !NT_SUCCESS( status ) )//成功.#define STATUS_INVALID_SID               ((NTSTATUS)0xC0000078L)   -1073741704
    //{
    //    return status;
    //}        

    KdPrint(("UserName:%wZ\n",&PUserInformation->UserName));
    //KdPrint(("LogonServer :%wZ\n",&PUserInformation->LogonServer));
    //KdPrint(("LogonDomainName :%wZ\n",&PUserInformation->LogonDomainName));
    //KdPrint(("SID :%wZ\n",&SidString));

    //RtlFreeUnicodeString(&SidString); 
    LsaFreeReturnBuffer(PUserInformation);

    PsDereferencePrimaryToken(pat);

    return status;
}

EX_CALLBACK_FUNCTION RegistryCallback;
NTSTATUS RegistryCallback(__in PVOID  CallbackContext, __in_opt PVOID  Argument1, __in_opt PVOID  Argument2)
{
    print_current_user();

    return STATUS_SUCCESS;
}

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

#pragma INITCODE
DRIVER_INITIALIZE DriverEntry;
NTSTATUS DriverEntry(__in struct _DRIVER_OBJECT * DriverObject, __in PUNICODE_STRING RegistryPath)
{
    KdBreakPoint();

    DriverObject->DriverUnload = Unload; 

    return CmRegisterCallback(RegistryCallback, NULL, &Cookie);
} 

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/*
文件名:RtlFormatCurrentUserKeyPath.C

功能:获取当前操作注册表的进程的用户的SID.

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

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

//#pragma comment (lib,"ksecdd.lib")

LARGE_INTEGER  Cookie;


//http://msdn.microsoft.com/en-us/library/ff899322(v=vs.85).aspx 
//Initializes the supplied buffer with a string representation of the SID for the current user.
//extern NTSTATUS /*WINAPI*/ RtlFormatCurrentUserKeyPath(/*_Out_*/  UNICODE_STRING CurrentUserKeyPath);  //此函数在XP就已经导出,但直接引用会出现链接错误。
typedef NTSTATUS (WINAPI * RtlFormatCurrentUserKeyPath)(/*_Out_*/  UNICODE_STRING * CurrentUserKeyPath); //那咱就动态获取吧!
//CurrentUserKeyPath [out]
//String that represents the current user's root key in the Registry. Caller must call RtlFreeUnicodeString to free the buffer when done with it.
/*
注意:微软的官方的链接里面是错误的。
开始我还以为这是我第一个发现参数传递结构,而不是指针的。
*/

RtlFormatCurrentUserKeyPath g_p_RtlFormatCurrentUserKeyPath;


PVOID ExpAllocateStringRoutine (IN SIZE_T NumberOfBytes)
{
    return ExAllocatePoolWithTag (PagedPool,NumberOfBytes,'grtS');
}
//上下的定义摘自:\wrk\WindowsResearchKernel-WRK\WRK-v1.2\base\ntos\ex\pool.c
#ifdef ALLOC_DATA_PRAGMA
#pragma const_seg("PAGECONST")
#endif
const PRTL_ALLOCATE_STRING_ROUTINE RtlAllocateStringRoutine = ExpAllocateStringRoutine;
const PRTL_FREE_STRING_ROUTINE RtlFreeStringRoutine = (PRTL_FREE_STRING_ROUTINE)ExFreePool;
#ifdef ALLOC_DATA_PRAGMA
#pragma const_seg()
#endif


// Maximum size of TOKEN_USER information.
#define SIZE_OF_TOKEN_INFORMATION                   \
    sizeof( TOKEN_USER )                            \
    + sizeof( SID )                                 \
    + sizeof( ULONG ) * SID_MAX_SUB_AUTHORITIES
//这个函数及上面的定义摘自WRK-v1.2/base/ntos/rtl/regutil.c,但是有修改,可以在驱动中使用或者利用。
NTSTATUS RtlFormatCurrentUserKeyPath0(OUT PUNICODE_STRING CurrentUserKeyPath)
/*++
Routine Description:
    Initialize the supplied buffer with a string representation of the current user's SID.
    这个函数还是有必要说明几点:
    1.要获取当前用户的SID,首先的不是判断SID而是当前用户,再获取SID这很容易。
      一台计算机同时登陆几个不同的用户是正常的。
      通常一个普通的用户一个会话,包括远程登录的。
      所以另一个捷径是判断会话,这有公开/导出的函数。如某个进程或者线程属于哪个会话。
    2.此函数有发掘利用的价值,看你怎么用了。
    3.此函数获取的不是当前登录用户的SID,而是当前的操作的进程的用户的SID,如系统帐户,网络帐户,服务帐户等。

Arguments:
    CurrentUserKeyPath - Returns a string that represents the current user's root key in the Registry.  
    Caller must call RtlFreeUnicodeString to free the buffer when done with it.

Return Value:
    NTSTATUS - Returns STATUS_SUCCESS if the user string was succesfully initialized.
--*/
{
    HANDLE TokenHandle = 0;
    UCHAR TokenInformation[ SIZE_OF_TOKEN_INFORMATION ];
    ULONG ReturnLength;
    ULONG SidStringLength = 0 ;//添加初始化为0.
    UNICODE_STRING SidString ;
    NTSTATUS Status;

    // Inside the kernel we can tell rapidly if we are impersonating.
    Status = STATUS_NO_TOKEN;
    if (PsIsThreadTerminating (PsGetCurrentThread ())) {//原来是PS_IS_THREAD_IMPERSONATING。
        Status = ZwOpenThreadTokenEx (NtCurrentThread(), TOKEN_READ, TRUE, OBJ_KERNEL_HANDLE, &TokenHandle);
        if ( !NT_SUCCESS( Status ) && ( Status != STATUS_NO_TOKEN ) ) {
            return Status;
        }
    }

    if ( !NT_SUCCESS( Status ) ) {
        Status = ZwOpenProcessTokenEx (NtCurrentProcess(), TOKEN_READ, OBJ_KERNEL_HANDLE, &TokenHandle);
        if ( !NT_SUCCESS( Status )) {
            return Status;
        }
    }

    Status = ZwQueryInformationToken( TokenHandle, TokenUser, TokenInformation, sizeof( TokenInformation ), &ReturnLength);
    ZwClose( TokenHandle );
    if ( !NT_SUCCESS( Status )) {
        return Status;
    }

    //Status = RtlLengthSidAsUnicodeString(((PTOKEN_USER)TokenInformation)->User.Sid, &SidStringLength);
    //if ( !NT_SUCCESS( Status ) ) {
    //    return Status ;
    //}
    //因为RtlLengthSidAsUnicodeString在XP上没有导出,所以用下面的两个函数替换。
    if (!RtlValidSid(((PTOKEN_USER)TokenInformation)->User.Sid)) {
        return Status ;
    }
    SidStringLength = RtlLengthSid(((PTOKEN_USER)TokenInformation)->User.Sid);
    if (!SidStringLength) {
        return Status ;
    }

    CurrentUserKeyPath->Length = 0;
    CurrentUserKeyPath->MaximumLength = (USHORT)(SidStringLength + sizeof( L"\\REGISTRY\\USER\\" ) + sizeof( UNICODE_NULL ));
    CurrentUserKeyPath->Buffer = (RtlAllocateStringRoutine)( CurrentUserKeyPath->MaximumLength );
    if (CurrentUserKeyPath->Buffer == NULL) {
        return STATUS_NO_MEMORY;
    }
        
    RtlAppendUnicodeToString( CurrentUserKeyPath, L"\\REGISTRY\\USER\\" );// Copy "\REGISTRY\USER" to the current user string.

    SidString.MaximumLength = CurrentUserKeyPath->MaximumLength;//(USHORT)SidStringLength ;这里有修改 原数乘以2应该也可以的,不然RtlConvertSidToUnicodeString失败。
    SidString.Length = 0 ;
    SidString.Buffer = CurrentUserKeyPath->Buffer + (CurrentUserKeyPath->Length / sizeof(WCHAR) );

    Status = RtlConvertSidToUnicodeString( &SidString, ((PTOKEN_USER)TokenInformation)->User.Sid, FALSE);
    if ( !NT_SUCCESS( Status )) {//#define STATUS_BUFFER_OVERFLOW           ((NTSTATUS)0x80000005L)
        RtlFreeUnicodeString( CurrentUserKeyPath );
    } else {
        CurrentUserKeyPath->Length += SidString.Length ;
    }

    return Status;
}


NTSTATUS print_current_user()
    /*
    打印当前操作的当前进程的用户名等.
    */
{
    NTSTATUS status = 0;
    UNICODE_STRING CurrentUserKeyPath = {0};

    status = g_p_RtlFormatCurrentUserKeyPath(&CurrentUserKeyPath);
    if( !NT_SUCCESS( status ) )
    {
        return status;
    }
    KdPrint(("CurrentUserKeyPath:%wZ\n",&CurrentUserKeyPath));//"\REGISTRY\USER\S-1-5-18"
    RtlFreeUnicodeString(&CurrentUserKeyPath);


    status = RtlFormatCurrentUserKeyPath0(&CurrentUserKeyPath);
    if( !NT_SUCCESS( status ) )
    {
        return status;
    }
    KdPrint(("CurrentUserKeyPath:%wZ\n",&CurrentUserKeyPath));//"\REGISTRY\USER\S-1-5-18"
    RtlFreeUnicodeString(&CurrentUserKeyPath);

    return status;
}

EX_CALLBACK_FUNCTION RegistryCallback;
NTSTATUS RegistryCallback(__in PVOID  CallbackContext, __in_opt PVOID  Argument1, __in_opt PVOID  Argument2)
{
    print_current_user();
    return STATUS_SUCCESS;
}

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

#pragma INITCODE
DRIVER_INITIALIZE DriverEntry;
NTSTATUS DriverEntry(__in struct _DRIVER_OBJECT * DriverObject, __in PUNICODE_STRING RegistryPath)
{
    UNICODE_STRING us_RtlFormatCurrentUserKeyPath;

    KdBreakPoint();

    DriverObject->DriverUnload = Unload; 

    RtlInitUnicodeString( &us_RtlFormatCurrentUserKeyPath, L"RtlFormatCurrentUserKeyPath" );
    g_p_RtlFormatCurrentUserKeyPath = MmGetSystemRoutineAddress(&us_RtlFormatCurrentUserKeyPath);
    if (g_p_RtlFormatCurrentUserKeyPath == NULL)
    {
        return STATUS_UNSUCCESSFUL;
    }

    return CmRegisterCallback(RegistryCallback, NULL, &Cookie);
} 

2013年11月13日星期三

创建注册表的根键

/*
功能:创建注册表的根键.

尽管:《深入解析windows操作系统》的第四章有如下说明:
There are six root keys (and you can't add new root keys or delete existing ones) that store information

RegLoadKey函数有如下说明:
Creates a subkey under HKEY_USERS or HKEY_LOCAL_MACHINE and loads the data from the specified registry hive into that subkey.

而且:http://msdn.microsoft.com/en-us/library/windows/hardware/ff546907(v=vs.85).aspx
的补充说明里面说不建议使用如下函数.
NtRestoreKey
NtSaveKey
NtSaveKeyEx
NtLoadKeyEx
NtUnloadKey2
NtUnloadKeyEx
NtReplaceKey
NtRenameKey
NtSetInformationKey

这些函数没有公开的调用接口,但是导出了,而且很早的版本就导出了,所以可以安心的使用.

注册表根键创建的成功与失败可以通过代码的返回值查看.
                          也可以通过注册表路径HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\hivelist查看.

不足之处,敬请指出.
made by correy
made at 2013.11.13
email:kouleguan at hotmail dot com
homepage:http://correy.webs.com                        
*/

///////////////////////////////////////////////////////////////////////////////////////////////////////////////
用户层创建注册表根键的代码.

#include <windows.h>

typedef _Return_type_success_(return >= 0) LONG NTSTATUS;
typedef NTSTATUS *PNTSTATUS;

typedef struct _UNICODE_STRING {
    USHORT Length;
    USHORT MaximumLength;
#ifdef MIDL_PASS
    [size_is(MaximumLength / 2), length_is((Length) / 2) ] USHORT * Buffer;
#else // MIDL_PASS
    _Field_size_bytes_part_(MaximumLength, Length) PWCH   Buffer;
#endif // MIDL_PASS
} UNICODE_STRING;
typedef UNICODE_STRING *PUNICODE_STRING;
typedef const UNICODE_STRING *PCUNICODE_STRING;

typedef struct _OBJECT_ATTRIBUTES {
    ULONG Length;
    HANDLE RootDirectory;
    PUNICODE_STRING ObjectName;
    ULONG Attributes;
    PVOID SecurityDescriptor;        // Points to type SECURITY_DESCRIPTOR
    PVOID SecurityQualityOfService;  // Points to type SECURITY_QUALITY_OF_SERVICE
} OBJECT_ATTRIBUTES;
typedef OBJECT_ATTRIBUTES *POBJECT_ATTRIBUTES;
typedef CONST OBJECT_ATTRIBUTES *PCOBJECT_ATTRIBUTES;

#ifdef __cplusplus
extern "C++"
{
char _RTL_CONSTANT_STRING_type_check(const char *s);
char _RTL_CONSTANT_STRING_type_check(const WCHAR *s);
// __typeof would be desirable here instead of sizeof.
template <size_t N> class _RTL_CONSTANT_STRING_remove_const_template_class;
template <> class _RTL_CONSTANT_STRING_remove_const_template_class<sizeof(char)>  {public: typedef  char T; };
template <> class _RTL_CONSTANT_STRING_remove_const_template_class<sizeof(WCHAR)> {public: typedef WCHAR T; };
#define _RTL_CONSTANT_STRING_remove_const_macro(s) \
    (const_cast<_RTL_CONSTANT_STRING_remove_const_template_class<sizeof((s)[0])>::T*>(s))
}
#else
char _RTL_CONSTANT_STRING_type_check(const void *s);
#define _RTL_CONSTANT_STRING_remove_const_macro(s) (s)
#endif
#define RTL_CONSTANT_STRING(s) \
{ \
    sizeof( s ) - sizeof( (s)[0] ), \
    sizeof( s ) / sizeof(_RTL_CONSTANT_STRING_type_check(s)), \
    _RTL_CONSTANT_STRING_remove_const_macro(s) \
}

#define InitializeObjectAttributes( p, n, a, r, s ) { \
    (p)->Length = sizeof( OBJECT_ATTRIBUTES );          \
    (p)->RootDirectory = r;                             \
    (p)->Attributes = a;                                \
    (p)->ObjectName = n;                                \
    (p)->SecurityDescriptor = s;                        \
    (p)->SecurityQualityOfService = NULL;               \
    }

#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)

#define OBJ_INHERIT             0x00000002L
#define OBJ_PERMANENT           0x00000010L
#define OBJ_EXCLUSIVE           0x00000020L
#define OBJ_CASE_INSENSITIVE    0x00000040L
#define OBJ_OPENIF              0x00000080L
#define OBJ_OPENLINK            0x00000100L
#define OBJ_KERNEL_HANDLE       0x00000200L
#define OBJ_FORCE_ACCESS_CHECK  0x00000400L
#define OBJ_VALID_ATTRIBUTES    0x000007F2L

//NTSTATUS ZwUnloadKey(IN POBJECT_ATTRIBUTES KeyObjectAttributes);
//NTSTATUS ZwLoadKey(IN POBJECT_ATTRIBUTES KeyObjectAttributes, IN POBJECT_ATTRIBUTES FileObjectAttributes);

//函数指针.
typedef NTSTATUS (WINAPI  * ZWLOADKEY) (IN POBJECT_ATTRIBUTES KeyObjectAttributes, IN POBJECT_ATTRIBUTES FileObjectAttributes);//__stdcall
typedef NTSTATUS (WINAPI * ZWUNLOADKEY) (IN POBJECT_ATTRIBUTES KeyObjectAttributes);//__stdcall

//全局变量
UNICODE_STRING uRegistryPath = RTL_CONSTANT_STRING(L"\\REGISTRY\\correy");
UNICODE_STRING uRegDatPath = RTL_CONSTANT_STRING(L"\\DosDevices\\c:\\correy.DAT");
OBJECT_ATTRIBUTES obj;
OBJECT_ATTRIBUTES HiveFile;
ZWUNLOADKEY ZwUnloadKey;
ZWLOADKEY ZwLoadKey;


BOOL SetPrivilege(
    //HANDLE hToken,          // access token handle
    LPCTSTR lpszPrivilege,  // name of privilege to enable/disable
    BOOL bEnablePrivilege   // to enable or disable privilege
    )
{
    TOKEN_PRIVILEGES tp;
    LUID luid;

    HANDLE hToken; // Get a token for this process.
    if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
    {
        return( FALSE );
    }

    if ( !LookupPrivilegeValue(
        NULL,            // lookup privilege on local system
        lpszPrivilege,   // privilege to lookup
        &luid ) )        // receives LUID of privilege
    {
        printf("LookupPrivilegeValue error: %u\n", GetLastError() );
        return FALSE;
    }

    tp.PrivilegeCount = 1;
    tp.Privileges[0].Luid = luid;
    if (bEnablePrivilege)
        tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    else
        tp.Privileges[0].Attributes = 0;

    // Enable the privilege or disable all privileges.
    if ( !AdjustTokenPrivileges(
        hToken,
        FALSE,
        &tp,
        sizeof(TOKEN_PRIVILEGES),
        (PTOKEN_PRIVILEGES) NULL,
        (PDWORD) NULL) )
    {
        printf("AdjustTokenPrivileges error: %u\n", GetLastError() );
        return FALSE;
    }

    if (GetLastError() == ERROR_NOT_ALL_ASSIGNED)
    {
        printf("The token does not have the specified privilege. \n");
        return FALSE;
    }

    return TRUE;
}


void main( )
{
    BOOL B;

    B = SetPrivilege(SE_BACKUP_NAME, TRUE);
    B = SetPrivilege(SE_RESTORE_NAME, TRUE);//必须加上这个RegSaveKey才成功.

    int r;

    LONG l = RegSaveKey(HKEY_CURRENT_USER, L"c:\\correy.dat", NULL);//如果已经存在,即使不在使用中,这个会失败.
    if (l != ERROR_SUCCESS)
    {
        r = GetLastError();
    }

    InitializeObjectAttributes(&obj,&uRegistryPath, OBJ_CASE_INSENSITIVE, NULL, NULL );//|OBJ_KERNEL_HANDLE
    InitializeObjectAttributes(&HiveFile,&uRegDatPath,OBJ_CASE_INSENSITIVE, NULL, NULL );//|OBJ_KERNEL_HANDLE

    HINSTANCE hinstLib = LoadLibrary(TEXT("ntdll.dll")); // Get a handle to the DLL module.    
    if (hinstLib != NULL) // If the handle is valid, try to get the function address.
    {
        ZwLoadKey = (ZWLOADKEY) GetProcAddress(hinstLib, "NtLoadKey");
        if (NULL != ZwLoadKey) // If the function address is valid, call the function.
        {
            NTSTATUS status = ZwLoadKey(&obj,&HiveFile);//只一行运行会出错.
            if(!NT_SUCCESS(status))
            {
                MessageBox(0,0,0,0);
                //return;
            }
        }  

        MessageBox(0,L"下面开始卸载",0,0);

        ZwUnloadKey = (ZWUNLOADKEY) GetProcAddress(hinstLib, "NtUnloadKey");
        if (NULL != ZwUnloadKey) // If the function address is valid, call the function.
        {
            NTSTATUS status = ZwUnloadKey(&obj);//只一行运行会出错.
            if(!NT_SUCCESS(status))
            {
                MessageBox(0,0,0,0);
                //return;
            }
        }

        FreeLibrary(hinstLib); // Free the DLL module.
    }

    //BOOL b = CopyFile(L"C:\\Users\\Administrator\\NTUSER.DAT", L"c:\\hkcu_copy.dat",false);
    //if (!b)
    //{
    //    r = GetLastError();//0x00000020,另一个程序(system)正在使用此文件,进程无法访问。
    //}
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
驱动层创建注册表根键的代码如下:

#include <ntifs.h>

NTSTATUS ZwUnloadKey(IN POBJECT_ATTRIBUTES KeyObjectAttributes);
NTSTATUS ZwLoadKey(IN POBJECT_ATTRIBUTES KeyObjectAttributes, IN POBJECT_ATTRIBUTES FileObjectAttributes);

UNICODE_STRING uRegistryPath = RTL_CONSTANT_STRING(L"\\REGISTRY\\correy");
UNICODE_STRING uRegDatPath = RTL_CONSTANT_STRING(L"\\DosDevices\\c:\\correy.DAT");//此文件必须是本计算机的,系统的或者自己生成的.
OBJECT_ATTRIBUTES obj;
OBJECT_ATTRIBUTES HiveFile;

VOID RegisterLoad()
{
    NTSTATUS status;
    HANDLE hRegister;
    ULONG i=0,ulSize=0;
    UNICODE_STRING us;

    status = ZwLoadKey(&obj,&HiveFile);
    if(!NT_SUCCESS(status))
    {
        DbgPrint("LoadKey failed Error: [%x] \n", status);
        return;
    }

    //一下是列举其子键的,也就是验证下.
    status = ZwOpenKey(&hRegister, KEY_ALL_ACCESS, &obj);
    if (NT_SUCCESS(status))
    {        
        PKEY_FULL_INFORMATION pfi;

        ZwQueryKey(hRegister, KeyFullInformation, NULL, 0, &ulSize);
        pfi = (PKEY_FULL_INFORMATION)ExAllocatePool(PagedPool, ulSize);// 第一次调用是为了获取需要的长度

        ZwQueryKey(hRegister, KeyFullInformation, pfi, ulSize, &ulSize);// 第二次调用是为了获取数据
        for (i = 0; i < pfi->SubKeys; i++)
        {    
            PKEY_BASIC_INFORMATION pbi;

            ZwEnumerateKey(hRegister, i, KeyBasicInformation, NULL, 0, &ulSize);// 获取第i个子项的长度
            pbi = (PKEY_BASIC_INFORMATION)ExAllocatePool(PagedPool, ulSize);        

            ZwEnumerateKey(hRegister, i, KeyBasicInformation, pbi, ulSize, &ulSize);// 获取第i个子项的数据

            us.Buffer = pbi->Name;
            us.Length = (USHORT)pbi->NameLength;
            us.MaximumLength = pbi->NameLength + sizeof(wchar_t);

            DbgPrint("The %d SubItem Name : %wZ\n", i, &us);

            ExFreePool(pbi);// 释放内存
        }

        ExFreePool(pfi);
        ZwClose(hRegister);
    } else {
        DbgPrint("ZwOpenKey failed unknown cause. Error: [%x] \n", status);
    }
}

DRIVER_UNLOAD Unload;
VOID Unload(__in PDRIVER_OBJECT DriverObject)
{  
    NTSTATUS status;  

    status = ZwUnloadKey(&obj);
    if(!NT_SUCCESS(status))
    {
        DbgPrint("ZwUnloadKey fail!\n");
    }
}

#pragma INITCODE
DRIVER_INITIALIZE DriverEntry;
NTSTATUS DriverEntry(__in struct _DRIVER_OBJECT * DriverObject, __in PUNICODE_STRING RegistryPath)
{
    NTSTATUS Status = 0;

    KdBreakPoint();// == DbgBreakPoint()

    DriverObject->DriverUnload = Unload;  

    InitializeObjectAttributes(&obj,&uRegistryPath, OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE, NULL, NULL );
    InitializeObjectAttributes(&HiveFile,&uRegDatPath,OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE, NULL, NULL );

    RegisterLoad();  

    return Status;
}

minifilter通讯之简单示例

应用层的代码如下:

/*
名字:minifilter通讯之简单示例.之所以说简单是因为没有列举异步,多线程等全面和安全的用法.

个人感觉FilterSendMessage就像DeviceIoControl,可以发送和接受信息.
        但是要自定义格式结构,驱动收到了再解析.

FilterGetMessage和FilterReplyMessage可能需要多线程或者异步等措施,这里不列举了,只是简单列举一下调用:

//FILTER_MESSAGE_HEADER  MessageBuffer;
//hResult = FilterGetMessage(port, &MessageBuffer, sizeof (FILTER_MESSAGE_HEADER), 0);
//if (IS_ERROR( hResult )) {
//    OutputDebugString(L"FilterGetMessage fail!\n");
//} else {
//    OutputDebugString(L"FilterGetMessage ok!\n");
//}

//FILTER_REPLY_HEADER  ReplyBuffer;
//hResult = FilterReplyMessage(port, &ReplyBuffer,sizeof(FILTER_REPLY_HEADER));
//if (IS_ERROR( hResult )) {
//    OutputDebugString(L"FilterReplyMessage fail!\n");
//} else {
//    OutputDebugString(L"FilterReplyMessage ok!\n");
//}

不足之处,敬请指出.
made by correy
made at 2013.11.13
email:kouleguan at hotmail dot com
homepage:http://correy.webs.com

效果如下(包括驱动打印的消息):
用户发来的信息是:test
[1124] FilterSendMessage ok!
[1124] 从内核发来的信息是:
[1124] to user client
[1124]

*/

#include <windows.h>

//这两个文件在VS中没有,在WDK中有.
//如果要用VS编译要拷贝相应的文件到相应的目录或者改变目录的设置等.
#include <fltuser.h>
#pragma comment(lib, "fltLib.lib")

int _tmain(int argc, _TCHAR* argv[])
{
    MessageBox(0,L"请附加调试器",L"调试专用",0);//如果是远程调试的话,这行特别有用.

    HANDLE port = INVALID_HANDLE_VALUE;
    HRESULT hResult = S_OK;

    hResult = FilterConnectCommunicationPort( L"\\CommunicationPort", 0, NULL, 0, NULL, &port );
    if (IS_ERROR( hResult )) {
        OutputDebugString(L"FilterConnectCommunicationPort fail!\n");
        return hResult;
    }

    wchar_t InBuffer[] = L"test";
    wchar_t OutBuffer[MAX_PATH] = {0};
    DWORD bytesReturned = 0;
    hResult = FilterSendMessage(port, InBuffer, lstrlen(InBuffer), OutBuffer, sizeof(OutBuffer), &bytesReturned);
    if (IS_ERROR( hResult )) {
        OutputDebugString(L"FilterSendMessage fail!\n");
        CloseHandle( port );
        return hResult;
    } else {
        OutputDebugString(L"FilterSendMessage ok!\n");
    }

    OutputDebugString(L"从内核发来的信息是:");
    OutputDebugString(OutBuffer);
    OutputDebugString(L"\n");

    CloseHandle( port );

 return 0;
}

驱动的代码如下:

/*
内核中没有:FltGetMessage,FltReplyMessage函数.
个人认为:MessageNotifyCallback有FltGetMessage,FltReplyMessage,FltSendMessage这三个函数的功能.
所以在MessageNotifyCallback里面调用这些函数是不对的,得到一些意想不到的效果.建议不要这样做.

FltGetMessage除了在MessageNotifyCallback里面,大多的地方都可以调用,但是用户层最好开启线程处理函数.

FltGetMessage函数调用示例代码如下:
//{
//    wchar_t SenderBuffer[] = L"SenderBuffer";
//    wchar_t ReplyBuffer[] = L"ReplyBuffer";
//    ULONG replyLength = sizeof(ReplyBuffer);
//
//    status = FltSendMessage( gFilterHandle, &g_ClientPort, SenderBuffer, sizeof(SenderBuffer), ReplyBuffer, &replyLength, NULL);
//    if (STATUS_SUCCESS == status) {
//        DbgPrint( "send message to user-mode\n");       
//    } else {
//        DbgPrint( "couldn't send message to user-mode to scan file, status 0x%X\n", status );
//    }
//}
*/

#include <fltKernel.h>

PFLT_FILTER gFilterHandle;
PFLT_PORT g_ServerPort;
PFLT_PORT g_ClientPort;

NTSTATUS MessageNotifyCallback (
    IN PVOID PortCookie,
    IN PVOID InputBuffer OPTIONAL,
    IN ULONG InputBufferLength,
    OUT PVOID OutputBuffer OPTIONAL,
    IN ULONG OutputBufferLength,//用户可以接受的数据的最大长度.
    OUT PULONG ReturnOutputBufferLength)
    /*
    这里要注意:1.数据地址的对齐.
               2.文档建议使用:try/except处理.
               3.如果是64位的驱动要考虑32位的EXE发来的请求.
    */
{
    NTSTATUS status = 0;
    wchar_t buffer[] = L"to user client";//
    
    PAGED_CODE();

    UNREFERENCED_PARAMETER(PortCookie);

    //打印用户发来的信息
    KdPrint(("用户发来的信息是:%ls\n",InputBuffer));

    //返回用户一些信息.
    *ReturnOutputBufferLength = sizeof(buffer);
    RtlCopyMemory(OutputBuffer,buffer,* ReturnOutputBufferLength);

    /*
    minispy在这里用FilterSendMessage获取信息的,对就是FilterSendMessage.
    这里某个类型里面获取信息,这些信息是在各种操作时(IRP的MJ_)加入链表的.
    注意链表的操作一定要同步,支持多线程.
    然后用户的一个线程在不停的获取这些信息.
    */

    return status;
}

VOID DisconnectNotifyCallback (_In_opt_ PVOID ConnectionCookie)
{
    PAGED_CODE();

    UNREFERENCED_PARAMETER(ConnectionCookie);

    FltCloseClientPort(gFilterHandle, &g_ClientPort);//应该加判断,如果ConnectionCookie == 我们的值就执行这行.
}

NTSTATUS ConnectNotifyCallback (IN PFLT_PORT ClientPort, IN PVOID ServerPortCookie, IN PVOID ConnectionContext, IN ULONG SizeOfContext, OUT PVOID * ConnectionPortCookie)
{
    PAGED_CODE();

    UNREFERENCED_PARAMETER( ServerPortCookie );
    UNREFERENCED_PARAMETER( ConnectionContext );
    UNREFERENCED_PARAMETER( SizeOfContext);
    UNREFERENCED_PARAMETER( ConnectionPortCookie);

    //可以加以判断,禁止非法的连接,从而给予保护.
    g_ClientPort = ClientPort;//保存以供以后使用.

    return STATUS_SUCCESS;
}

#pragma PAGEDCODE
NTSTATUS PtInstanceQueryTeardown (__in PCFLT_RELATED_OBJECTS FltObjects,__in FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags)
{
    return STATUS_SUCCESS;
}

#pragma PAGEDCODE//#pragma alloc_text(PAGE, PtUnload)
NTSTATUS PtUnload (__in FLT_FILTER_UNLOAD_FLAGS Flags)
{
    FltCloseCommunicationPort(g_ServerPort);//没有这一行是停止不了驱动的,查询也是永远等待中.
    FltUnregisterFilter( gFilterHandle );
    return STATUS_SUCCESS;
}

CONST FLT_REGISTRATION FilterRegistration = {
    sizeof( FLT_REGISTRATION ),         //  Size
    FLT_REGISTRATION_VERSION,           //  Version
    0,                                  //  Flags
    NULL,                               //  Context
    NULL,                               //  Operation callbacks
    PtUnload,                           //  MiniFilterUnload
    NULL,                               //  InstanceSetup
    PtInstanceQueryTeardown,            //  InstanceQueryTeardown
    NULL,                               //  InstanceTeardownStart
    NULL,                               //  InstanceTeardownComplete
    NULL,                               //  GenerateFileName
    NULL,                               //  GenerateDestinationFileName
    NULL                                //  NormalizeNameComponent
};

DRIVER_INITIALIZE DriverEntry;
#pragma alloc_text(INIT, DriverEntry)//#pragma INITCODE
NTSTATUS DriverEntry (_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath)
{
    NTSTATUS status;
    PSECURITY_DESCRIPTOR sd;
    OBJECT_ATTRIBUTES oa;
    UNICODE_STRING uniString;

    UNREFERENCED_PARAMETER(RegistryPath);

    KdBreakPoint();

    __try
    {
        status = FltRegisterFilter(DriverObject, &FilterRegistration, &gFilterHandle); 
        if (!NT_SUCCESS(status)) //;
        {       
            __leave;
        }

        status  = FltBuildDefaultSecurityDescriptor(&sd, FLT_PORT_ALL_ACCESS);
        if (!NT_SUCCESS( status )) {
            __leave;
        }

        RtlInitUnicodeString(&uniString, L"\\CommunicationPort");
        InitializeObjectAttributes( &oa, &uniString, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, sd);
        status = FltCreateCommunicationPort(gFilterHandle, &g_ServerPort, &oa, NULL, ConnectNotifyCallback, DisconnectNotifyCallback, MessageNotifyCallback, 1);
        FltFreeSecurityDescriptor( sd );
        if (!NT_SUCCESS( status )) {
            __leave;
        }

        status = FltStartFiltering(gFilterHandle);//这个结果在下面判断.
    } __finally {
        if (!NT_SUCCESS( status ) )
        {
            if (NULL != g_ServerPort) {
                FltCloseCommunicationPort(g_ServerPort);
            }

            if (NULL != gFilterHandle) {
                FltUnregisterFilter(gFilterHandle);
            }
        }       
    }

    return status;
}

2013年10月23日星期三

枚举系统注册的所有的minifilter驱动及其信息

/*
文件名:FltEnumerateFilters.C

微软不建议使用:IoEnumerateRegisteredFiltersList.

功能:enumerates all registered minifilter drivers in the system及其一些信息的显示.
目的:示例一些minifilter的一些枚举函数的用法,理解,区别.

可能要连接fltMgr.lib.
加载的方式可用一般的驱动(nt服务).

参考资料:
http://hi.baidu.com/kernelkit/item/95dab957bf115711aaf6d743
http://www.inreverse.net/?p=1334
http://blogs.msdn.com/b/alexcarp/archive/2009/08/11/issuing-io-in-minifilters-part-1-fltcreatefile.aspx 
http://blogs.msdn.com/b/alexcarp/archive/2009/09/01/issuing-io-in-minifilters-part-2-flt-vs-zw.aspx
http://blogs.msdn.com/b/alexcarp/archive/2009/06/16/filter-manager-concepts-part-3-flt-filter.aspx
http://blogs.msdn.com/b/alexcarp/archive/2009/06/24/filter-manager-concepts-part-4-flt-instance.aspx
等. 

不足之处,敬请指出.
made by correy
made at 2013.10.23
email:kouleguan at hotmail dot com
homepage:http://correy.webs.com

个人感觉FltEnumerateInstances和FltEnumerateVolumes差不多.
区别是前者能获取所有的minifilter的所有的实例的信息,后者只获取某个minifilter的所有的卷设备的信息.
相信有转换和联系的函数.

开始以为:FltEnumerateVolumes是获取的一个PFLT_FILTER过滤的卷设备的对象,不是全部的磁盘上的卷.
经测试是:获取本地计算机上的可用的卷.一下是获取不到的:
1.光驱里面没有光盘.
2.虚拟磁盘软件虚拟出来的卷设备.
3.网络映射的盘符.
4.subst命令搞出的驱动器号.
5.软盘没有测试.
这个函数的缺点是第一个参数,而且还不能是0,个人建议是去掉这个参数.
不过在minifilter驱动里面是有这个参数的.
*/

#include <fltKernel.h>

#define TAG 'tset' //test
#define _In_ //WDK8不需要.

void PrintFilterFullInformation(PFLT_FILTER pf)
{
    //另一个思路是使用:FltEnumerateFilterInformation

    NTSTATUS status = STATUS_SUCCESS;
    PVOID  Buffer = 0;
    ULONG  BufferSize = 0;
    ULONG  BytesReturned = 0;
    PFILTER_FULL_INFORMATION pfi = 0;
    UNICODE_STRING FilterName;

    status = FltGetFilterInformation(pf, FilterFullInformation, Buffer, BufferSize, &BytesReturned);
    if (!NT_SUCCESS( status )) 
    {
        if (status != STATUS_BUFFER_TOO_SMALL)
        {
            return;
        }
    }

    BufferSize = sizeof (PFLT_FILTER) * BytesReturned * 2;//多申请一倍.

    Buffer = (PFLT_FILTER *)ExAllocatePoolWithTag(NonPagedPool, BufferSize, TAG);
    if (Buffer == NULL) {
        return;
    }
    RtlZeroMemory(Buffer,BufferSize);

    status = FltGetFilterInformation(pf, FilterFullInformation, Buffer, BufferSize, &BytesReturned);
    if (!NT_SUCCESS( status )) 
    {
        ExFreePoolWithTag(Buffer, TAG);
        return;
    }

    pfi = (PFILTER_FULL_INFORMATION)Buffer;

    FilterName.Buffer = pfi->FilterNameBuffer;
    FilterName.Length = pfi->FilterNameLength;
    FilterName.MaximumLength = pfi->FilterNameLength;//不再加2.

    //DbgPrint("FrameID:%d\n", pfi->FrameID);
    //DbgPrint("NumberOfInstances:%d\n", pfi->NumberOfInstances);
    DbgPrint("FilterName:%wZ\n",&FilterName);

    /*
    打印的内容如下:
    FrameID:0
    NumberOfInstances:5
    FilterName:TFsFlt
    FrameID:0
    NumberOfInstances:6
    FilterName:QQSysMonX64
    FrameID:0
    NumberOfInstances:1
    FilterName:luafv 注释:微软的LUA文件虚拟化筛选器驱动程序.
    */
    
    ExFreePoolWithTag(Buffer, TAG);
}

void PrintVolumeStandardInformation(PFLT_VOLUME pv)
{
    NTSTATUS status = STATUS_SUCCESS;
    PVOID  Buffer = 0;
    ULONG  BufferSize = 0;
    ULONG  BytesReturned = 0;
    PFILTER_VOLUME_STANDARD_INFORMATION pvsi = 0;
    UNICODE_STRING VolumeName;

    status = FltGetVolumeInformation(pv, FilterVolumeStandardInformation, Buffer, BufferSize, &BytesReturned);
    if (!NT_SUCCESS( status )) 
    {
        if (status != STATUS_BUFFER_TOO_SMALL)
        {
            return;
        }
    }

    BufferSize = sizeof (PFLT_FILTER) * BytesReturned * 2;//多申请一倍.

    Buffer = (PFLT_FILTER *)ExAllocatePoolWithTag(NonPagedPool, BufferSize, TAG);
    if (Buffer == NULL) {
        return;
    }
    RtlZeroMemory(Buffer,BufferSize);

    status = FltGetVolumeInformation(pv, FilterVolumeStandardInformation, Buffer, BufferSize, &BytesReturned);
    if (!NT_SUCCESS( status )) 
    {
        ExFreePoolWithTag(Buffer, TAG);
        return;
    }

    pvsi = (PFILTER_VOLUME_STANDARD_INFORMATION)Buffer;

    VolumeName.Buffer = pvsi->FilterVolumeName;
    VolumeName.Length = pvsi->FilterVolumeNameLength;
    VolumeName.MaximumLength = pvsi->FilterVolumeNameLength;//不再加2.

    //DbgPrint("Flags:%d\n", pvsi->Flags);
    //DbgPrint("FrameID:%d\n", pvsi->FrameID);
    DbgPrint("VolumeName:%wZ\n",&VolumeName);

    switch(pvsi->FileSystemType) 
    {
    case FLT_FSTYPE_UNKNOWN:
        DbgPrint("FileSystemType :%ls\n",L"Unknown file system type.");
        break;
    case FLT_FSTYPE_RAW: 
        DbgPrint("FileSystemType :%ls\n",L"\\FileSystem\\RAW.");
        break;
    case FLT_FSTYPE_NTFS: 
        DbgPrint("FileSystemType :%ls\n",L"\\FileSystem\\Ntfs.");
        break;
    case FLT_FSTYPE_FAT: 
        DbgPrint("FileSystemType :%ls\n",L"\\FileSystem\\Fat.");
        break;
    case FLT_FSTYPE_CDFS:
        DbgPrint("FileSystemType :%ls\n",L"\\FileSystem\\Cdfs.");
        break;
    case FLT_FSTYPE_UDFS: 
        DbgPrint("FileSystemType :%ls\n",L"\\FileSystem\\Udfs.");
        break;
    case FLT_FSTYPE_LANMAN: 
        DbgPrint("FileSystemType :%ls\n",L"\\FileSystem\\MRxSmb.");
        break;
    case FLT_FSTYPE_WEBDAV: 
        DbgPrint("FileSystemType :%ls\n",L"\\FileSystem\\MRxDav.");
        break;
    case FLT_FSTYPE_RDPDR:
        DbgPrint("FileSystemType :%ls\n",L"\\Driver\\rdpdr.");
        break;
    case FLT_FSTYPE_NFS: 
        DbgPrint("FileSystemType :%ls\n",L"\\FileSystem\\NfsRdr.");
        break;
    case FLT_FSTYPE_MS_NETWARE: 
        DbgPrint("FileSystemType :%ls\n",L"\\FileSystem\\nwrdr.");
        break;
    case FLT_FSTYPE_NETWARE: 
        DbgPrint("FileSystemType :%ls\n",L"Novell NetWare redirector.");
        break;
    case FLT_FSTYPE_BSUDF:
        DbgPrint("FileSystemType :%ls\n",L"\\FileSystem\\BsUDF.");
        break;
    case FLT_FSTYPE_MUP:
        DbgPrint("FileSystemType :%ls\n",L"\\FileSystem\\Mup.");
        break;
    case FLT_FSTYPE_RSFX: 
        DbgPrint("FileSystemType :%ls\n",L"\\FileSystem\\RsFxDrv.");
        break;
    case FLT_FSTYPE_ROXIO_UDF1:
        DbgPrint("FileSystemType :%ls\n",L"\\FileSystem\\cdudf_xp.");
        break;
    case FLT_FSTYPE_ROXIO_UDF2:
        DbgPrint("FileSystemType :%ls\n",L"\\FileSystem\\UdfReadr_xp.");
        break;
    case FLT_FSTYPE_ROXIO_UDF3: 
        DbgPrint("FileSystemType :%ls\n",L"\\FileSystem\\DVDVRRdr_xp.");
        break;
    case FLT_FSTYPE_TACIT: 
        DbgPrint("FileSystemType :%ls\n",L"\\Device\\TCFSPSE.");
        break;
    case FLT_FSTYPE_FS_REC: 
        DbgPrint("FileSystemType :%ls\n",L"\\FileSystem\\Fs_rec.");
        break;
    case FLT_FSTYPE_INCD:
        DbgPrint("FileSystemType :%ls\n",L"\\FileSystem\\InCDfs.");
        break;
    case FLT_FSTYPE_INCD_FAT: 
        DbgPrint("FileSystemType :%ls\n",L"\\FileSystem\\InCDFat.");
        break;
    case FLT_FSTYPE_EXFAT: 
        DbgPrint("FileSystemType :%ls\n",L"\\FileSystem\\exfat.");
        break;
    case FLT_FSTYPE_PSFS: 
        DbgPrint("FileSystemType :%ls\n",L"\\FileSystem\\psfs.");
        break;
    case FLT_FSTYPE_GPFS:
        DbgPrint("FileSystemType :%ls\n",L"\\FileSystem\\gpfs.");
        break;
    case FLT_FSTYPE_NPFS: 
        DbgPrint("FileSystemType :%ls\n",L"\\FileSystem\\npfs.");
        break;
    case FLT_FSTYPE_MSFS: 
        DbgPrint("FileSystemType :%ls\n",L"\\FileSystem\\msfs.");
        break;
    case FLT_FSTYPE_CSVFS: 
        DbgPrint("FileSystemType :%ls\n",L"\\FileSystem\\csvfs.");
        break;
    case FLT_FSTYPE_REFS: 
        DbgPrint("FileSystemType :%ls\n",L"\\FileSystem\\refs.");
        break;
    case FLT_FSTYPE_OPENAFS: 
        DbgPrint("FileSystemType :%ls\n",L"\\FileSystem\\AFSRedirector.");
        break;
    default: 
        DbgPrint("FileSystemType :%ls\n",L"发生错误!");
        break;
    }

    //这里也要用FltObjectDereference再释放一下?
    FltObjectDereference(pv);

    ExFreePoolWithTag(Buffer, TAG);
}

void EnumerateInstances(PFLT_FILTER pf)
{
    //FltEnumerateInstances
    //FltGetInstanceInformation 这个获取的全是数字,所以放弃使用.
    //FltEnumerateInstanceInformationByFilter 这个获取的全是数字,所以放弃使用.
    //FltEnumerateInstanceInformationByVolume 这个获取的全是数字,所以放弃使用.

    NTSTATUS status = STATUS_SUCCESS;
    PFLT_INSTANCE  * InstanceList = 0;
    ULONG  InstanceListSize = 0;
    ULONG  NumberInstancesReturned = 0;
    ULONG i;

    status = FltEnumerateInstances(0, pf, InstanceList, InstanceListSize, &NumberInstancesReturned);
    if (!NT_SUCCESS( status )) 
    {
        if (status != STATUS_BUFFER_TOO_SMALL)
        {
            return;
        }
    }

    InstanceListSize = sizeof (PFLT_INSTANCE) * NumberInstancesReturned * 2;//多申请一倍.

    InstanceList = (PFLT_INSTANCE *)ExAllocatePoolWithTag(NonPagedPool, InstanceListSize, TAG);
    if (InstanceList == NULL) {
        return;
    }
    RtlZeroMemory(InstanceList,InstanceListSize);

    status = FltEnumerateInstances(0, pf, InstanceList, InstanceListSize, &NumberInstancesReturned);
    if (!NT_SUCCESS( status )) 
    {
        ExFreePoolWithTag(InstanceList, TAG);
        return;
    }

    for (i = 0; i< NumberInstancesReturned; i++)
    {
        //打印每个实例的信息.
        //相信和卷设备是一样的,转换为卷设备再打印,这里就不打印详细信息的,只打印实例的地址.
        DbgPrint("PFLT_FILTER:%p\tInstances:%p\n", pf, InstanceList[i]);

        FltObjectDereference(InstanceList[i]);
    }

    //这里也要用FltObjectDereference再释放一下?
    FltObjectDereference(pf);

    ExFreePoolWithTag(InstanceList, TAG);
}

void EnumerateVolumes(PFLT_FILTER pf)
{
    //FltEnumerateVolumes这个获取全了,要枚举.
    //FltGetVolumeInformation
    //FltEnumerateVolumeInformation这个不用,是获取单个的,要循环.

    NTSTATUS status = STATUS_SUCCESS;
    PFLT_VOLUME  * VolumeList = 0;
    ULONG  VolumeListSize = 0;
    ULONG  NumberVolumesReturned = 0;
    ULONG i;

    status = FltEnumerateVolumes(pf, VolumeList, VolumeListSize, &NumberVolumesReturned);
    if (!NT_SUCCESS( status )) 
    {
        if (status != STATUS_BUFFER_TOO_SMALL)
        {
            return;
        }
    }

    VolumeListSize = sizeof (PFLT_VOLUME) * NumberVolumesReturned * 2;//多申请一倍.

    VolumeList = (PFLT_VOLUME  *)ExAllocatePoolWithTag(NonPagedPool, VolumeListSize, TAG);
    if (VolumeList == NULL) {
        return;
    }
    RtlZeroMemory(VolumeList,VolumeListSize);

    status = FltEnumerateVolumes(pf, VolumeList, VolumeListSize, &NumberVolumesReturned);
    if (!NT_SUCCESS( status )) 
    {
        ExFreePoolWithTag(VolumeList, TAG);
        return;
    }

    for (i = 0; i< NumberVolumesReturned; i++)
    {
        //打印每个卷设备的信息.
        PrintVolumeStandardInformation(VolumeList[i]);

        FltObjectDereference(VolumeList[i]);
    }

    //这里也要用FltObjectDereference再释放一下?
    FltObjectDereference(pf);

    ExFreePoolWithTag(VolumeList, TAG);
}

VOID DriverUnload(IN PDRIVER_OBJECT DriverObject)
{
    /*
    没有此函数是停止不了此驱动的,但是可以删除驱动.
    在别的地方设置一下,如注册表回调保护驱动入口的第二个参数,可以让本驱动删除不了.
    如果这样,就在一个消息中卸载了:恢复回调入口和删除注册表的保护.
    然后应用层再卸载,上面的消息中还可以加密码验证.
    */
}

DRIVER_INITIALIZE DriverEntry;
#pragma alloc_text(INIT, DriverEntry)//#pragma INITCODE
NTSTATUS DriverEntry (_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath)
{
    NTSTATUS status = STATUS_SUCCESS;
    PFLT_FILTER * FilterList;
    ULONG FilterListSize = 0;
    ULONG NumberFiltersReturned = 0;
    ULONG i;

    UNREFERENCED_PARAMETER( RegistryPath );

    KdBreakPoint();//DbgBreakPoint() 

    DriverObject->DriverUnload = DriverUnload;

    //Because filters can register at any time, two calls to FltEnumerateFilters are not guaranteed to return the same result.
    //确保两次调用FltEnumerateFilters期间不要加载或者卸载minifilter.建议使用rundown机制.
    status = FltEnumerateFilters(0,FilterListSize,&NumberFiltersReturned);
    if (!NT_SUCCESS( status )) //#define STATUS_BUFFER_TOO_SMALL          ((NTSTATUS)0xC0000023L)
    {
        if (status != STATUS_BUFFER_TOO_SMALL)
        {
            return status;
        }
    }    

    //建议每次成功的调用之后都调用:VOID FltObjectDereference(_Inout_  PVOID FltObject);

    FilterListSize = sizeof (PFLT_FILTER) * NumberFiltersReturned * 2;//多申请一倍.

    FilterList = (PFLT_FILTER *)ExAllocatePoolWithTag(NonPagedPool, FilterListSize, TAG);
    if (FilterList == NULL) {
        return status;
    }
    RtlZeroMemory(FilterList,FilterListSize);

    status = FltEnumerateFilters(FilterList,FilterListSize,&NumberFiltersReturned);
    if (!NT_SUCCESS( status )) 
    {
        ExFreePoolWithTag(FilterList, TAG);
        return status;
    }

    //卸载所有已经注册的minifilter.理论上比抹去未公开的结构好.这里暂时注释掉.
    //for (i = 0;i < NumberFiltersReturned;i++)
    //{
    //    FltUnregisterFilter(FilterList[i]);//有的驱动会永远停止在这里.
    //}

    /*
    在这里可以列举一些minifilter的信息
    甚至每个minifilter过滤的每个卷设备的信息.

    PFLT_FILTER是个为公开的数据结构,各个版本的结构不一.

    0: kd> vertarget
    Windows 7 Kernel Version 7601 (Service Pack 1) MP (2 procs) Free x64
    Built by: 7601.18229.amd64fre.win7sp1_gdr.130801-1533
    Machine Name:
    Kernel base = 0xfffff800`0185f000 PsLoadedModuleList = 0xfffff800`01aa26d0
    Debug session time: Wed Oct 23 16:25:06.039 2013 (UTC + 8:00)
    System Uptime: 0 days 0:04:08.664
    0: kd> dt _FLT_FILTER fffffa80`04306c60
    fltmgr!_FLT_FILTER
    +0x000 Base             : _FLT_OBJECT
    +0x020 Frame            : 0xfffffa80`03f30630 _FLTP_FRAME
    +0x028 Name             : _UNICODE_STRING "TFsFlt"
    +0x038 DefaultAltitude  : _UNICODE_STRING "389700"
    +0x048 Flags            : 2 ( FLTFL_FILTERING_INITIATED )
    +0x050 DriverObject     : 0xfffffa80`042fde70 _DRIVER_OBJECT
    +0x058 InstanceList     : _FLT_RESOURCE_LIST_HEAD
    +0x0d8 VerifierExtension : (null) 
    +0x0e0 VerifiedFiltersLink : _LIST_ENTRY [ 0x00000000`00000000 - 0x00000000`00000000 ]
    +0x0f0 FilterUnload     : 0xfffff880`0245f320     long  +0
    +0x0f8 InstanceSetup    : 0xfffff880`0248d0ec     long  +0
    +0x100 InstanceQueryTeardown : 0xfffff880`0248d118     long  +0
    +0x108 InstanceTeardownStart : (null) 
    +0x110 InstanceTeardownComplete : 0xfffff880`0248d138     void  +0
    +0x118 SupportedContextsListHead : 0xfffffa80`04306890 _ALLOCATE_CONTEXT_HEADER
    +0x120 SupportedContexts : [6] (null) 
    +0x150 PreVolumeMount   : (null) 
    +0x158 PostVolumeMount  : (null) 
    +0x160 GenerateFileName : (null) 
    +0x168 NormalizeNameComponent : (null) 
    +0x170 NormalizeNameComponentEx : (null) 
    +0x178 NormalizeContextCleanup : (null) 
    +0x180 KtmNotification  : (null) 
    +0x188 Operations       : 0xfffffa80`04306ef0 _FLT_OPERATION_REGISTRATION
    +0x190 OldDriverUnload  : (null) 
    +0x198 ActiveOpens      : _FLT_MUTEX_LIST_HEAD
    +0x1e8 ConnectionList   : _FLT_MUTEX_LIST_HEAD
    +0x238 PortList         : _FLT_MUTEX_LIST_HEAD
    +0x288 PortLock         : _EX_PUSH_LOCK   

    再附加两个结构:
    1: kd> dt _FLT_INSTANCE
    fltmgr!_FLT_INSTANCE
    +0x000 Base             : _FLT_OBJECT
    +0x020 OperationRundownRef : Ptr64 _EX_RUNDOWN_REF_CACHE_AWARE
    +0x028 Volume           : Ptr64 _FLT_VOLUME
    +0x030 Filter           : Ptr64 _FLT_FILTER
    +0x038 Flags            : _FLT_INSTANCE_FLAGS
    +0x040 Altitude         : _UNICODE_STRING
    +0x050 Name             : _UNICODE_STRING
    +0x060 FilterLink       : _LIST_ENTRY
    +0x070 ContextLock      : _EX_PUSH_LOCK
    +0x078 Context          : Ptr64 _CONTEXT_NODE
    +0x080 TransactionContexts : _CONTEXT_LIST_CTRL
    +0x088 TrackCompletionNodes : Ptr64 _TRACK_COMPLETION_NODES
    +0x090 CallbackNodes    : [50] Ptr64 _CALLBACK_NODE
    1: kd> dt _FLT_VOLUME
    fltmgr!_FLT_VOLUME
    +0x000 Base             : _FLT_OBJECT
    +0x020 Flags            : _FLT_VOLUME_FLAGS
    +0x024 FileSystemType   : _FLT_FILESYSTEM_TYPE
    +0x028 DeviceObject     : Ptr64 _DEVICE_OBJECT
    +0x030 DiskDeviceObject : Ptr64 _DEVICE_OBJECT
    +0x038 FrameZeroVolume  : Ptr64 _FLT_VOLUME
    +0x040 VolumeInNextFrame : Ptr64 _FLT_VOLUME
    +0x048 Frame            : Ptr64 _FLTP_FRAME
    +0x050 DeviceName       : _UNICODE_STRING
    +0x060 GuidName         : _UNICODE_STRING
    +0x070 CDODeviceName    : _UNICODE_STRING
    +0x080 CDODriverName    : _UNICODE_STRING
    +0x090 InstanceList     : _FLT_RESOURCE_LIST_HEAD
    +0x110 Callbacks        : _CALLBACK_CTRL
    +0x4f8 ContextLock      : _EX_PUSH_LOCK
    +0x500 VolumeContexts   : _CONTEXT_LIST_CTRL
    +0x508 StreamListCtrls  : _FLT_RESOURCE_LIST_HEAD
    +0x588 FileListCtrls    : _FLT_RESOURCE_LIST_HEAD
    +0x608 NameCacheCtrl    : _NAME_CACHE_VOLUME_CTRL
    +0x6b8 MountNotifyLock  : _ERESOURCE
    +0x720 TargetedOpenActiveCount : Int4B
    +0x728 TxVolContextListLock : _EX_PUSH_LOCK
    +0x730 TxVolContexts    : _TREE_ROOT

    不过建议使用:FltEnumerateFilterInformation或者FltGetFilterInformation获取各种信息.
    */

    //打印每个驱动的信息,这里选择FilterFullInformation类型.
    for (i = 0;i < NumberFiltersReturned;i++)
    {
        PrintFilterFullInformation(FilterList[i]);//打印系统的所有的minifilter 驱动.
        EnumerateInstances(FilterList[i]);//枚举每个minifilter驱动的每个过滤设备的实例,里面可以获取更多的信息.
        EnumerateVolumes(FilterList[i]);//枚举每个minifilter驱动的每个卷设备的信息,里面可以获取更多的信息,其实和上面的差不多.

        DbgPrint("\n\n");
    }

    /*
    FltEnumerateFilters adds a rundown reference to each of the opaque filter pointers returned in the array that the FilterList parameter points to. 
    When these pointers are no longer needed, the caller must release them by calling FltObjectDereference on each one. 
    Thus every successful call to FltEnumerateFilters must be matched by a subsequent call to FltObjectDereference for each returned filter pointer. 
    */
    for (i = 0;i < NumberFiltersReturned;i++)
    {
        FltObjectDereference(FilterList[i]);
    }    

    ExFreePoolWithTag(FilterList, TAG);

    return status;
}

/*
效果如下:
0: kd> g
FilterName:TFsFlt
PFLT_FILTER:FFFFFA800433E010 Instances:FFFFFA8004432C60
PFLT_FILTER:FFFFFA800433E010 Instances:FFFFFA8004346C60
PFLT_FILTER:FFFFFA800433E010 Instances:FFFFFA80047D3C60
PFLT_FILTER:FFFFFA800433E010 Instances:FFFFFA80047B9C60
PFLT_FILTER:FFFFFA800433E010 Instances:FFFFFA80040DF7F0
VolumeName:\Device\Mup
FileSystemType :\FileSystem\Mup.
VolumeName:\Device\HarddiskVolume1
FileSystemType :\FileSystem\Ntfs.
VolumeName:\Device\HarddiskVolume2
FileSystemType :\FileSystem\Ntfs.
VolumeName:\Device\HarddiskVolume5
FileSystemType :\FileSystem\Ntfs.
VolumeName:\Device\HarddiskVolume3
FileSystemType :\FileSystem\Ntfs.
VolumeName:\Device\HarddiskVolume4
FileSystemType :\FileSystem\Ntfs.


FilterName:QQSysMonX64
PFLT_FILTER:FFFFFA8004A8B160 Instances:FFFFFA8004A9A760
PFLT_FILTER:FFFFFA8004A8B160 Instances:FFFFFA8004A9D760
PFLT_FILTER:FFFFFA8004A8B160 Instances:FFFFFA8004AA0C50
PFLT_FILTER:FFFFFA8004A8B160 Instances:FFFFFA8004AA0170
PFLT_FILTER:FFFFFA8004A8B160 Instances:FFFFFA8004AA34F0
PFLT_FILTER:FFFFFA8004A8B160 Instances:FFFFFA8004AA4C50
VolumeName:\Device\Mup
FileSystemType :\FileSystem\Mup.
VolumeName:\Device\HarddiskVolume1
FileSystemType :\FileSystem\Ntfs.
VolumeName:\Device\HarddiskVolume2
FileSystemType :\FileSystem\Ntfs.
VolumeName:\Device\HarddiskVolume5
FileSystemType :\FileSystem\Ntfs.
VolumeName:\Device\HarddiskVolume3
FileSystemType :\FileSystem\Ntfs.
VolumeName:\Device\HarddiskVolume4
FileSystemType :\FileSystem\Ntfs.


FilterName:luafv
PFLT_FILTER:FFFFFA8004A8A010 Instances:FFFFFA8004A8C010
VolumeName:\Device\Mup
FileSystemType :\FileSystem\Mup.
VolumeName:\Device\HarddiskVolume1
FileSystemType :\FileSystem\Ntfs.
VolumeName:\Device\HarddiskVolume2
FileSystemType :\FileSystem\Ntfs.
VolumeName:\Device\HarddiskVolume5
FileSystemType :\FileSystem\Ntfs.
VolumeName:\Device\HarddiskVolume3
FileSystemType :\FileSystem\Ntfs.
VolumeName:\Device\HarddiskVolume4
FileSystemType :\FileSystem\Ntfs.

验证一下:
0: kd> !fltkd.filters

Filter List: fffffa8003e690c0 "Frame 0" 
   FLT_FILTER: fffffa800433e010 "TFsFlt" "389700"
      FLT_INSTANCE: fffffa8004432c60 "TFsFlt Instance" "389700"
      FLT_INSTANCE: fffffa8004346c60 "TFsFlt Instance" "389700"
      FLT_INSTANCE: fffffa80047d3c60 "TFsFlt Instance" "389700"
      FLT_INSTANCE: fffffa80047b9c60 "TFsFlt Instance" "389700"
      FLT_INSTANCE: fffffa80040df7f0 "TFsFlt Instance" "389700"
   FLT_FILTER: fffffa8004a8b160 "QQSysMonX64" "327125"
      FLT_INSTANCE: fffffa8004a9a760 "QQSysMonx64 Instance" "327125"
      FLT_INSTANCE: fffffa8004a9d760 "QQSysMonx64 Instance" "327125"
      FLT_INSTANCE: fffffa8004aa0c50 "QQSysMonx64 Instance" "327125"
      FLT_INSTANCE: fffffa8004aa0170 "QQSysMonx64 Instance" "327125"
      FLT_INSTANCE: fffffa8004aa34f0 "QQSysMonx64 Instance" "327125"
      FLT_INSTANCE: fffffa8004aa4c50 "QQSysMonx64 Instance" "327125"
   FLT_FILTER: fffffa8004a8a010 "luafv" "135000"
      FLT_INSTANCE: fffffa8004a8c010 "luafv" "135000"

0: kd> !fltkd.instance fffffa8004432c60

FLT_INSTANCE: fffffa8004432c60 "TFsFlt Instance" "389700"
   FLT_OBJECT: fffffa8004432c60  [01000000] Instance
      RundownRef               : 0x0000000000000000 (0)
      PointerCount             : 0x00000002 
      PrimaryLink              : [fffffa8004a9d770-fffffa800404d108] 
   OperationRundownRef      : fffffa80044310b0 
Could not read field "Number" of fltmgr!_EX_RUNDOWN_REF_CACHE_AWARE from address: fffffa80044310b0
   Flags                    : [00000000]
   Volume                   : fffffa800404d010 "\Device\HarddiskVolume1"
   Filter                   : fffffa800433e010 "TFsFlt"
   TrackCompletionNodes     : fffffa8004432a20 
   ContextLock              : (fffffa8004432cd0)
   Context                  : fffffa800433a770 
   CallbackNodes            : (fffffa8004432cf0)
   VolumeLink               : [fffffa8004a9d770-fffffa800404d108] 
   FilterLink               : [fffffa8004346cc0-fffffa800433e0d0] 

*/

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

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

#define TAG 'tset' //test

/*
IoEnumerateRegisteredFiltersList enumerates only file system filter drivers (also called "legacy filters"). 
It does not enumerate minifilters. 
To enumerate both minifilters and legacy filters, or only minifilters, call FltEnumerateFilterInformation.

此代码在Windows 7上测试成功。
不当之处,请指正。

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


VOID DriverUnload(IN PDRIVER_OBJECT DriverObject)
{

}


DRIVER_INITIALIZE DriverEntry;
#pragma alloc_text(INIT, DriverEntry)//#pragma INITCODE
NTSTATUS DriverEntry (_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath)
{
    NTSTATUS status = STATUS_SUCCESS;
    PDRIVER_OBJECT * DriverObjectList = 0;//*
    ULONG DriverObjectListSize = 0;
    ULONG ActualNumberDriverObjects = 0;
    ULONG i = 0;

    UNREFERENCED_PARAMETER( RegistryPath );

    KdBreakPoint();

    DriverObject->DriverUnload = DriverUnload;

    //XP下调用此函数没有反应。就是各个参数及返回值依旧是原来的样子。
    status = IoEnumerateRegisteredFiltersList(DriverObjectList, DriverObjectListSize, &ActualNumberDriverObjects);
    if (!NT_SUCCESS( status )) 
    {
        if (status != STATUS_BUFFER_TOO_SMALL)
        {
            return status;
        }
    }

    //XP下申请大小为0的内存竟然成功,而且还可以读写。
    DriverObjectListSize = ActualNumberDriverObjects * sizeof(DRIVER_OBJECT);
    DriverObjectList = (PDRIVER_OBJECT *)ExAllocatePoolWithTag(NonPagedPool, DriverObjectListSize, TAG);
    if (DriverObjectList == NULL) {
        return STATUS_UNSUCCESSFUL ;
    }
    RtlZeroMemory(DriverObjectList, DriverObjectListSize);

    //XP下调用此函数仍然没有反应。就是各个参数及返回值依旧是原来的样子。
    status = IoEnumerateRegisteredFiltersList(DriverObjectList, DriverObjectListSize, &ActualNumberDriverObjects);
    if (!NT_SUCCESS( status )) 
    {
        return status;
    }

    for (i = 0;i < ActualNumberDriverObjects;i++)
    {
        DbgPrint("DriverName:%wZ\n",&DriverObjectList[i]->DriverName);
        ObDereferenceObject(DriverObjectList[i]);
    }    

    ExFreePoolWithTag(DriverObjectList, TAG);

    return status;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/*WindowsResearchKernel-WRK/WRK-v1.2/base/ntos/io/iomgr/iosubs.c
NTSTATUS
IoEnumerateRegisteredFiltersList(
    IN  PDRIVER_OBJECT  *DriverObjectList,
    IN  ULONG           DriverObjectListSize,
    OUT PULONG          ActualNumberDriverObjects
    )
++

Routine Description:

    This routine retrieves a list of driver objects associated with all of the
    filters which have registered with the system.

Parameters:

    DriverObjectList - Pointer to an array where driver  object lists will be stored.

    DriverObjectListSize - Size in bytes of the DriverObjectList array

    ActualNumberDriverObjects - The actual number of driver objects returned.

ReturnValue:

    If size is not sufficient it will return STATUS_BUFFER_TOO_SMALL.

Notes:

--*//*
{
    PNOTIFICATION_PACKET nPacket;
    PLIST_ENTRY entry;
    ULONG   numListEntries;
    ULONG   numDriverObjects = 0;
    NTSTATUS status = STATUS_SUCCESS;

    //
    //  Lock the list shared
    //

    ExAcquireResourceSharedLite( &IopDatabaseResource, TRUE );

    //
    //  Calculate how many entries can be returned
    //

    numListEntries = DriverObjectListSize / sizeof(PDRIVER_OBJECT);

    //
    //  Calculate how many entries are on the list
    //

    entry = IopFsNotifyChangeQueueHead.Flink;
    while (entry != &IopFsNotifyChangeQueueHead) {

        numDriverObjects++;
        entry = entry->Flink;
    }

    //
    //  Return total number of entries on the list
    //

    *ActualNumberDriverObjects = numDriverObjects;

    //
    //  If we don't have room for all of the entries, tell them about this
    //

    if (numDriverObjects > numListEntries) {
        status = STATUS_BUFFER_TOO_SMALL;
    }

    //
    //  Return what entries we can
    //

    entry = IopFsNotifyChangeQueueHead.Flink;

    while ((numListEntries > 0) && (entry != &IopFsNotifyChangeQueueHead)) {

        nPacket = CONTAINING_RECORD( entry, NOTIFICATION_PACKET, ListEntry );

        ObReferenceObject(nPacket->DriverObject);

        *DriverObjectList = nPacket->DriverObject;
        DriverObjectList++;

        entry = entry->Flink;
        numListEntries--;
    }

    ExReleaseResourceLite( &IopDatabaseResource );

    return status;
}
*/