2014年7月24日星期四

内核中的注册表操作

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

/*
Zw层次的注册表操作很简单。
但是做与不做还是有点区别的。

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

NTSTATUS ZwEnumerateKeyEx(IN UNICODE_STRING * Name)
    /*
    显示一个注册表的键下的:子键,名字,类型,数据。
    注意:没有递归显示。
    */
{
    NTSTATUS Status = STATUS_UNSUCCESSFUL;
    OBJECT_ATTRIBUTES        ObjectAttributes;
    HANDLE  KeyHandle;
    PKEY_FULL_INFORMATION pfi;    
    ULONG  ResultLength;
    ULONG i = 0;

    InitializeObjectAttributes(&ObjectAttributes, Name, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
    Status = ZwOpenKey(&KeyHandle, KEY_READ, &ObjectAttributes);
    if( !NT_SUCCESS( Status ) )
    {
        return Status;
    }

    /*
    注意ZwQueryKey的第一个参数。
    The KeyHandle passed to ZwQueryKey must have been opened with KEY_QUERY_VALUE access. 
    This is accomplished by passing KEY_QUERY_VALUE, KEY_READ, or KEY_ALL_ACCESS as the DesiredAccess parameter to ZwCreateKey or ZwOpenKey.
    */

    // 第一次调用是为了获取需要的长度
    Status = ZwQueryKey(KeyHandle, KeyFullInformation, NULL, 0, &ResultLength);
    if( !NT_SUCCESS( Status ) )
    {
        if (Status == STATUS_BUFFER_TOO_SMALL  || Status == STATUS_BUFFER_OVERFLOW) //STATUS_BUFFER_OVERFLOW这个情况应该不会发生在这种情况下。
        {
            //在下面申请内存。
        }
        else
        {
            ZwClose(KeyHandle);
            return Status;
        }
    }

    //ResultLength += MAX_PATH ;
    //ResultLength *= 2;//多申请一半。
    pfi = (PKEY_FULL_INFORMATION)ExAllocatePool(NonPagedPool, ResultLength);
    if (pfi == NULL)
    {
        //If ExAllocatePool returns NULL, the caller should return the NTSTATUS value STATUS_INSUFFICIENT_RESOURCES or should delay processing to another point in time.
        Status = STATUS_INSUFFICIENT_RESOURCES;
        ZwClose(KeyHandle);
        return Status;
    }

    // 第二次调用是为了获取数据
    Status = ZwQueryKey(KeyHandle, KeyFullInformation, pfi, ResultLength, &ResultLength);//少了赋值。这等低级的错误。
    if( !NT_SUCCESS( Status ) )
    {
        ExFreePool(pfi);
        ZwClose(KeyHandle);
        return Status;
    }

    //枚举子键。
    for (i = 0; i < pfi->SubKeys; i++)
    {     
        PKEY_BASIC_INFORMATION pbi;
        UNICODE_STRING us;

        // 获取第i个子项的长度
        Status = ZwEnumerateKey(KeyHandle, i, KeyBasicInformation, NULL, 0, &ResultLength);
        if( !NT_SUCCESS( Status ) )
        {
            if (Status == STATUS_BUFFER_TOO_SMALL  || Status == STATUS_BUFFER_OVERFLOW) //STATUS_BUFFER_OVERFLOW这个情况应该不会发生在这种情况下。
            {
                //在下面申请内存。
            }
            else
            {
                break;
            }
        }

        pbi = (PKEY_BASIC_INFORMATION)ExAllocatePool(NonPagedPool, ResultLength); 
        if (pbi == NULL)
        {
            Status = STATUS_INSUFFICIENT_RESOURCES;
            break;
        }

        // 获取第i个子项的数据
        Status = ZwEnumerateKey(KeyHandle, i, KeyBasicInformation, pbi, ResultLength, &ResultLength);
        if( !NT_SUCCESS( Status ) )
        {
            ExFreePool(pbi);
            break;
        }

        us.Buffer = pbi->Name;
        us.Length = (USHORT)pbi->NameLength;
        us.MaximumLength = us.Length;
        
        DbgPrint("subkey:%wZ\n", &us); 

        /*
        在这里组合字符串,可以考虑递归枚举。
        */

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

    //枚举名字,类型,数据。
    for (i = 0; i < pfi->Values; i++) //可以考虑用ZwQueryValueKey获取数量。MSDN关于这个成员的解释是:The number of value entries for this key.
    {     
        PKEY_VALUE_BASIC_INFORMATION pkvbi;
        UNICODE_STRING us;
        PKEY_VALUE_PARTIAL_INFORMATION pkvpi;
        UNICODE_STRING data;

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

        // 获取名字及类型。
        Status = ZwEnumerateValueKey(KeyHandle, i, KeyValueBasicInformation, NULL, 0, &ResultLength);
        if( !NT_SUCCESS( Status ) )
        {
            if (Status == STATUS_BUFFER_TOO_SMALL  || Status == STATUS_BUFFER_OVERFLOW) //STATUS_BUFFER_OVERFLOW这个情况应该不会发生在这种情况下。
            {
                //在下面申请内存。
            }
            else
            {
                break;
            }
        }
        pkvbi = (PKEY_VALUE_BASIC_INFORMATION)ExAllocatePool(NonPagedPool, ResultLength); 
        if (pkvbi == NULL)
        {
            Status = STATUS_INSUFFICIENT_RESOURCES;
            break;
        }
        Status = ZwEnumerateValueKey(KeyHandle, i, KeyValueBasicInformation, pkvbi, ResultLength, &ResultLength);
        if( !NT_SUCCESS( Status ) )
        {
            ExFreePool(pkvbi);
            break;
        }

        us.Buffer = pkvbi->Name;
        us.Length = (USHORT)pkvbi->NameLength;
        us.MaximumLength = us.Length;

        //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // 获取数据
        Status = ZwEnumerateValueKey(KeyHandle, i, KeyValuePartialInformation, NULL, 0, &ResultLength);
        if( !NT_SUCCESS( Status ) )
        {
            if (Status == STATUS_BUFFER_TOO_SMALL  || Status == STATUS_BUFFER_OVERFLOW) //STATUS_BUFFER_OVERFLOW这个情况应该不会发生在这种情况下。
            {
                //在下面申请内存。
            }
            else
            {
                ExFreePool(pkvbi);
                break;
            }
        }
        pkvpi = (PKEY_VALUE_PARTIAL_INFORMATION)ExAllocatePool(NonPagedPool, ResultLength); 
        if (pkvpi == NULL)
        {
            Status = STATUS_INSUFFICIENT_RESOURCES;
            ExFreePool(pkvbi);
            break;
        }
        Status = ZwEnumerateValueKey(KeyHandle, i, KeyValuePartialInformation, pkvpi, ResultLength, &ResultLength);
        if( !NT_SUCCESS( Status ) )
        {
            ExFreePool(pkvpi);
            ExFreePool(pkvbi);
            break;
        }

        data.Buffer = (PWCH)pkvpi->Data;//有的数据可能无法显示。
        data.Length = (USHORT)pkvpi->DataLength;
        data.MaximumLength = data.Length;

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

        DbgPrint("name:%wZ,type:%d,data:%wZ\n", &us, pkvbi->Type, &data); 

        ExFreePool(pkvbi);// 释放内存
        ExFreePool(pkvpi);
    }

    ExFreePool(pfi);
    ZwClose(KeyHandle);

    return Status;
}


DRIVER_UNLOAD Unload;
VOID Unload(__in PDRIVER_OBJECT DriverObject)
{

}


DRIVER_INITIALIZE DriverEntry;
NTSTATUS DriverEntry( __in struct _DRIVER_OBJECT  * DriverObject, __in PUNICODE_STRING  RegistryPath)
{
    NTSTATUS status = STATUS_UNSUCCESSFUL; 
    UNICODE_STRING test = RTL_CONSTANT_STRING(L"\\REGISTRY\\MACHINE\\SYSTEM\\CurrentControlSet\\Control");//\\Session Manager
    
    KdBreakPoint();

    DriverObject->DriverUnload = Unload;  

    status = ZwEnumerateKeyEx(&test);
    if( !NT_SUCCESS( status ) )
    {
        DbgPrint("ZwEnumerateKeyEx fail with 0x%x\n", status); 
    }   

    return status;//STATUS_SUCCESS
} 

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

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

#define TAG 'tset' //test


NTSTATUS ZwCopyKey(IN UNICODE_STRING * Name, IN UNICODE_STRING * Name2)
    /*
    复制一个注册表的键下的:子键,名字,类型,数据。
    注意:
    1.没有递归复制。
    2.没有复制(安全)属性。
    3.没有对参数的有效性进行检查。字符串的末尾不要带L'\\'.
    4.确认使用前这两个路径是存在的。
    5.更多的缺陷,请你补充纠正。更多的功能等待你的发挥。
    */
{
    NTSTATUS Status = STATUS_UNSUCCESSFUL;
    OBJECT_ATTRIBUTES        ObjectAttributes;
    HANDLE  KeyHandle;
    HANDLE  KeyHandle3;
    PKEY_FULL_INFORMATION pfi;    
    ULONG  ResultLength;
    ULONG i = 0;

    InitializeObjectAttributes(&ObjectAttributes, Name, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
    Status = ZwOpenKey(&KeyHandle, KEY_ALL_ACCESS, &ObjectAttributes);
    if( !NT_SUCCESS( Status ) )
    {
        return Status;
    }

    /*
    注意ZwQueryKey的第一个参数。
    The KeyHandle passed to ZwQueryKey must have been opened with KEY_QUERY_VALUE access. 
    This is accomplished by passing KEY_QUERY_VALUE, KEY_READ, or KEY_ALL_ACCESS as the DesiredAccess parameter to ZwCreateKey or ZwOpenKey.
    */

    // 第一次调用是为了获取需要的长度
    Status = ZwQueryKey(KeyHandle, KeyFullInformation, NULL, 0, &ResultLength);
    if( !NT_SUCCESS( Status ) )
    {
        if (Status == STATUS_BUFFER_TOO_SMALL  || Status == STATUS_BUFFER_OVERFLOW) //STATUS_BUFFER_OVERFLOW这个情况应该不会发生在这种情况下。
        {
            //在下面申请内存。
        }
        else
        {
            ZwClose(KeyHandle);
            return Status;
        }
    }

    //ResultLength += MAX_PATH ;
    //ResultLength *= 2;//多申请一半。
    pfi = (PKEY_FULL_INFORMATION)ExAllocatePool(NonPagedPool, ResultLength);
    if (pfi == NULL)
    {
        //If ExAllocatePool returns NULL, the caller should return the NTSTATUS value STATUS_INSUFFICIENT_RESOURCES or should delay processing to another point in time.
        Status = STATUS_INSUFFICIENT_RESOURCES;
        ZwClose(KeyHandle);
        return Status;
    }

    // 第二次调用是为了获取数据
    Status = ZwQueryKey(KeyHandle, KeyFullInformation, pfi, ResultLength, &ResultLength);//少了赋值。这等低级的错误。
    if( !NT_SUCCESS( Status ) )
    {
        ExFreePool(pfi);
        ZwClose(KeyHandle);
        return Status;
    }

    //枚举子键。
    for (i = 0; i < pfi->SubKeys; i++)
    {     
        PKEY_BASIC_INFORMATION pbi;
        UNICODE_STRING us;
        UNICODE_STRING new_key ;
        OBJECT_ATTRIBUTES ob;
        HANDLE KeyHandle2 = 0;
        PUNICODE_STRING Class = NULL;
        ULONG  Disposition;

        // 获取第i个子项的长度
        Status = ZwEnumerateKey(KeyHandle, i, KeyBasicInformation, NULL, 0, &ResultLength);
        if( !NT_SUCCESS( Status ) )
        {
            if (Status == STATUS_BUFFER_TOO_SMALL  || Status == STATUS_BUFFER_OVERFLOW) //STATUS_BUFFER_OVERFLOW这个情况应该不会发生在这种情况下。
            {
                //在下面申请内存。
            }
            else
            {
                break;
            }
        }

        pbi = (PKEY_BASIC_INFORMATION)ExAllocatePool(NonPagedPool, ResultLength); 
        if (pbi == NULL)
        {
            Status = STATUS_INSUFFICIENT_RESOURCES;
            break;
        }

        // 获取第i个子项的数据
        Status = ZwEnumerateKey(KeyHandle, i, KeyBasicInformation, pbi, ResultLength, &ResultLength);
        if( !NT_SUCCESS( Status ) )
        {
            ExFreePool(pbi);
            break;
        }

        us.Buffer = pbi->Name;
        us.Length = (USHORT)pbi->NameLength;
        us.MaximumLength = us.Length;
        
        DbgPrint("subkey:%wZ\n", &us); 

        //开始新建。

        new_key.Buffer = (wchar_t *)ExAllocatePoolWithTag(NonPagedPool, MAX_PATH, TAG);
        if (new_key.Buffer == NULL) { 
            ExFreePool(pbi);
            Status = STATUS_INSUFFICIENT_RESOURCES;
            break;
        }
        RtlZeroMemory(new_key.Buffer, MAX_PATH);
        RtlInitEmptyUnicodeString(&new_key, new_key.Buffer,MAX_PATH);

        RtlCopyUnicodeString(&new_key,Name2); 

        Status = RtlAppendUnicodeToString(&new_key, L"\\");
        if (!NT_SUCCESS (Status)) {
            ExFreePool(new_key.Buffer);
            ExFreePool(pbi);
            Status = STATUS_INSUFFICIENT_RESOURCES;
            break;
        }         

        Status = RtlAppendUnicodeStringToString(&new_key, &us);
        if (!NT_SUCCESS (Status)) {
            ExFreePool(new_key.Buffer);
            ExFreePool(pbi);
            Status = STATUS_INSUFFICIENT_RESOURCES;
            break;
        } 

        InitializeObjectAttributes(&ob, &new_key, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, 0, 0);
        Status = ZwCreateKey(&KeyHandle2, KEY_WRITE, &ob, 0, Class, REG_OPTION_NON_VOLATILE, &Disposition);//KEY_ALL_ACCESS KEY_READ
        if (!NT_SUCCESS (Status)) 
        {
            //如果子键已经存在,返回正确。
            ExFreePool(new_key.Buffer);
            ExFreePool(pbi);
            Status = STATUS_INSUFFICIENT_RESOURCES;
            break;
        }
        else
        {
            if (KeyHandle2)//断言FileHandle不等于0也不是无效的句柄。
            {
                Status = ZwClose(KeyHandle2);
                if (!NT_SUCCESS (Status)) 
                {
                    KdPrint(("ZwClose fail with 0x%x.\n", Status));
                }
            }
        }

        /*
        在这里组合字符串,可以考虑递归。
        */

        ExFreePool(pbi);// 释放内存
        ExFreePool(new_key.Buffer);
    }

    //处理上面失败的情况。主要是for 循环。
    if( !NT_SUCCESS( Status ) )
    {
        ExFreePool(pfi);
        ZwClose(KeyHandle);
        return Status;
    }

    InitializeObjectAttributes(&ObjectAttributes, Name2, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
    Status = ZwOpenKey(&KeyHandle3, KEY_ALL_ACCESS, &ObjectAttributes);
    if( !NT_SUCCESS( Status ) )
    {
        ExFreePool(pfi);
        ZwClose(KeyHandle);
        return Status;
    }

    //枚举名字,类型,数据。
    for (i = 0; i < pfi->Values; i++) //可以考虑用ZwQueryValueKey获取数量。MSDN关于这个成员的解释是:The number of value entries for this key.
    {     
        PKEY_VALUE_BASIC_INFORMATION pkvbi;
        UNICODE_STRING us;
        PKEY_VALUE_PARTIAL_INFORMATION pkvpi;
        UNICODE_STRING data;

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

        // 获取名字及类型。
        Status = ZwEnumerateValueKey(KeyHandle, i, KeyValueBasicInformation, NULL, 0, &ResultLength);
        if( !NT_SUCCESS( Status ) )
        {
            if (Status == STATUS_BUFFER_TOO_SMALL  || Status == STATUS_BUFFER_OVERFLOW) //STATUS_BUFFER_OVERFLOW这个情况应该不会发生在这种情况下。
            {
                //在下面申请内存。
            }
            else
            {
                break;
            }
        }
        pkvbi = (PKEY_VALUE_BASIC_INFORMATION)ExAllocatePool(NonPagedPool, ResultLength); 
        if (pkvbi == NULL)
        {
            Status = STATUS_INSUFFICIENT_RESOURCES;
            break;
        }
        Status = ZwEnumerateValueKey(KeyHandle, i, KeyValueBasicInformation, pkvbi, ResultLength, &ResultLength);
        if( !NT_SUCCESS( Status ) )
        {
            ExFreePool(pkvbi);
            break;
        }

        us.Buffer = pkvbi->Name;
        us.Length = (USHORT)pkvbi->NameLength;
        us.MaximumLength = us.Length;

        //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // 获取数据
        Status = ZwEnumerateValueKey(KeyHandle, i, KeyValuePartialInformation, NULL, 0, &ResultLength);
        if( !NT_SUCCESS( Status ) )
        {
            if (Status == STATUS_BUFFER_TOO_SMALL  || Status == STATUS_BUFFER_OVERFLOW) //STATUS_BUFFER_OVERFLOW这个情况应该不会发生在这种情况下。
            {
                //在下面申请内存。
            }
            else
            {
                ExFreePool(pkvbi);
                break;
            }
        }
        pkvpi = (PKEY_VALUE_PARTIAL_INFORMATION)ExAllocatePool(NonPagedPool, ResultLength); 
        if (pkvpi == NULL)
        {
            Status = STATUS_INSUFFICIENT_RESOURCES;
            ExFreePool(pkvbi);
            break;
        }
        Status = ZwEnumerateValueKey(KeyHandle, i, KeyValuePartialInformation, pkvpi, ResultLength, &ResultLength);
        if( !NT_SUCCESS( Status ) )
        {
            ExFreePool(pkvpi);
            ExFreePool(pkvbi);
            break;
        }

        data.Buffer = (PWCH)pkvpi->Data;//有的数据可能无法显示。
        data.Length = (USHORT)pkvpi->DataLength;
        data.MaximumLength = data.Length;

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

        Status = ZwSetValueKey(KeyHandle3, &us, 0, pkvbi->Type, data.Buffer, data.Length);
        if( !NT_SUCCESS( Status ) ) //如果句柄的权限是KEY_READ这里成功,但是实际是没有成功的。
        {
            ExFreePool(pkvpi);
            ExFreePool(pkvbi);
            break;
        }

        DbgPrint("name:%wZ,type:%d,data:%wZ\n", &us, pkvbi->Type, &data); 

        ExFreePool(pkvbi);// 释放内存
        ExFreePool(pkvpi);
    }

    ExFreePool(pfi);
    ZwClose(KeyHandle);
    ZwClose(KeyHandle3);

    return Status;
}


DRIVER_UNLOAD Unload;
VOID Unload(__in PDRIVER_OBJECT DriverObject)
{

}


DRIVER_INITIALIZE DriverEntry;
NTSTATUS DriverEntry( __in struct _DRIVER_OBJECT  * DriverObject, __in PUNICODE_STRING  RegistryPath)
{
    NTSTATUS status = STATUS_UNSUCCESSFUL; 
    UNICODE_STRING test = RTL_CONSTANT_STRING(L"\\REGISTRY\\MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Session Manager");
    UNICODE_STRING test2 = RTL_CONSTANT_STRING(L"\\REGISTRY\\MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Session Manager2");
    
    KdBreakPoint();

    DriverObject->DriverUnload = Unload;  

    status = ZwCopyKey(&test, &test2);
    if( !NT_SUCCESS( status ) )
    {
        DbgPrint("ZwEnumerateKeyEx fail with 0x%x\n", status); 
    }   

    return status;//STATUS_SUCCESS
} 

2014年7月22日星期二

最简单的显示网页的办法

#include "stdafx.h"

/*
显示网页的最简单的办法.
再复杂一点的是调用接口。

这个还有更多的属性去开发,如:查看源代码。
其实只有一个SYSLINK可用那个控件。
更多功能和优缺点有待深入。

本文修改自MSDN。
更加详细的是:ShowHTMLDialog Sample Source Page,
地址是:http://www.microsoft.com/en-us/download/details.aspx?id=944
名字是:htmldlg.exe,可解压释放。
这个好像显示的是本地的(PE文件的一个资源)自己的网页。还可以获取选择/操作的结果。

更多的类似的函数还有:
ShowHTMLDialogEx 
ShowModelessHTMLDialog 

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

#include <windows.h>
#include <urlmon.h>
#include <mshtmhst.h>

#pragma comment (lib,"Urlmon.lib")


void wmain(void)
{
    HINSTANCE hinstMSHTML = LoadLibrary(TEXT("MSHTML.DLL"));
    if (hinstMSHTML == NULL)
    {        
        return;// Error loading module -- fail as securely as possible
    }

    SHOWHTMLDIALOGFN* pfnShowHTMLDialog;
    pfnShowHTMLDialog = (SHOWHTMLDIALOGFN*)GetProcAddress(hinstMSHTML, "ShowHTMLDialog");
    if (pfnShowHTMLDialog)
    {
        IMoniker *pURLMoniker;
        BSTR bstrURL = SysAllocString(L"http://correy.webs.com");
        CreateURLMoniker(NULL, bstrURL, &pURLMoniker);
        if (pURLMoniker)
        {
            (*pfnShowHTMLDialog)(NULL, pURLMoniker, NULL, NULL, NULL);
            pURLMoniker->Release();
        }

        SysFreeString(bstrURL);
    }

    FreeLibrary(hinstMSHTML);

    //以下也是摘自MSDN。
    //IHostDialogHelper* pHDH;
    //IMoniker* pUrlMoniker;
    //BSTR bstrOptions = SysAllocString(L"dialogHeight:30;dialogWidth:40");
    //BSTR bstrPath = SysAllocString(L"c:\\dummy.htm");//c:\\dummy.htm http://correy.webs.com

    //CreateURLMoniker(NULL, bstrPath, &pUrlMoniker);
    //CoCreateInstance(CLSID_HostDialogHelper, NULL, CLSCTX_INPROC, IID_IHostDialogHelper, (void**)&pHDH);
    //pHDH->ShowHTMLDialog(NULL, pUrlMoniker, NULL, bstrOptions, NULL, NULL);//pHDH == 0

    //SysFreeString(bstrPath);
    //SysFreeString(bstrOptions);
    //pUrlMoniker->Release();
    //pHDH->Release();
}

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

#include "stdafx.h"

/*
改进版的如下:
在自己的窗口中显示一个网页(其实不是的,还得用接口)。

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

#include <windows.h>
#include <urlmon.h>
#include <mshtmhst.h>

#pragma comment (lib,"Urlmon.lib")

#pragma comment(linker, "/ENTRY:Entry") 
#pragma comment(linker, "/subsystem:windows")


void show_my_website(HWND hWnd)
{
    HINSTANCE hinstMSHTML = LoadLibrary(TEXT("MSHTML.DLL"));
    if (hinstMSHTML == NULL)
    {        
        return;// Error loading module -- fail as securely as possible
    }

    SHOWHTMLDIALOGFN* pfnShowHTMLDialog;
    pfnShowHTMLDialog = (SHOWHTMLDIALOGFN*)GetProcAddress(hinstMSHTML, "ShowHTMLDialog");
    if (pfnShowHTMLDialog)
    {
        IMoniker *pURLMoniker;
        BSTR bstrURL = SysAllocString(L"http://correy.webs.com");
        CreateURLMoniker(NULL, bstrURL, &pURLMoniker);
        if (pURLMoniker)
        {
            (*pfnShowHTMLDialog)(hWnd, pURLMoniker, NULL, NULL, NULL);
            pURLMoniker->Release();
        }

        SysFreeString(bstrURL);
    }

    FreeLibrary(hinstMSHTML);
}


LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 
{ 
    switch (uMsg) 
    { 
    case WM_CREATE://可以考虑刷新的消息。
        show_my_website(hWnd);
        break;
    case WM_DESTROY: 
        PostQuitMessage(0); 
        break; 
    default: 
        return(DefWindowProc(hWnd, uMsg, wParam, lParam)); 
    } 
    return(0); 
} 


void Entry() 
{   
    WNDCLASSEX sWndClassEx = {48,3,WindowProc,0,0,GetModuleHandle(0),0,LoadCursor(0,IDC_ARROW),(HBRUSH)6,0,L"correy",0}; 
    ATOM a = RegisterClassEx(&sWndClassEx); 
    ShowWindow(CreateWindowEx(0,L"correy",L"correy",WS_OVERLAPPEDWINDOW,CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,0,0, GetModuleHandle(0),0),1); 

    MSG sMsg; 
    while (GetMessage(&sMsg, NULL, 0, 0)) 
    { 
        DispatchMessage(&sMsg); 
    } 

    ExitProcess(0);
} 

调试sysenter

很早就有个想法:
看看sysenter指令的下一步操作是啥,也就是下一条指令的代码。

sysenter指令主要的操作是设置IP。
IP设置完后,这个指令的功能也就完成差不多了。
剩下的事是CPU的事了,也就是按照指定的地址去执行了。

这个指令的功能很明确了。

接下来进入正题:
1: kd> rdmsr 176 
msr[176] = 00000000`805426e0

注释:SYSENTER_EIP_MSR == 176

1: kd> u 805426e0
nt!KiFastCallEntry:
805426e0 b923000000      mov     ecx,23h
805426e5 6a30            push    30h
805426e7 0fa1            pop     fs
805426e9 8ed9            mov     ds,cx
805426eb 8ec1            mov     es,cx
805426ed 648b0d40000000  mov     ecx,dword ptr fs:[40h]
805426f4 8b6104          mov     esp,dword ptr [ecx+4]
805426f7 6a23            push    23h

至此问题应该解决了。

注意:有的时候,按进入下一步的时候不是这个地址,而是别的,我的解释是线程的切换。

而调用者一般如下:
ntdll!KiFastSystemCall:
                           001b:7c92e510 8bd4            mov     edx,esp
                           001b:7c92e512 0f34            sysenter
ntdll!KiFastSystemCallRet:
                           001b:7c92e514 c3              ret

------------------------------------------------------------------------------------------------------------------------------
nt!KiSystemCallExit:
                           805428ec cf              iretd 
nt!KiSystemCallExit2:
                           805428ed f644240901      test    byte ptr [esp+9],1
                           805428f2 75f8            jne     nt!KiSystemCallExit (805428ec)
                           805428f4 5a              pop     edx
                           805428f5 83c404          add     esp,4
                           805428f8 80642401fd      and     byte ptr [esp+1],0FDh
                           805428fd 9d              popfd
                           805428fe 59              pop     ecx
                           805428ff fb              sti
                           80542900 0f35            sysexit
                           80542902 cf              iretd
                           80542903 90              nop

1: kd> r
eax=00000000 ebx=7c9a0600 ecx=0225f624 edx=7c92e514 esi=0016b6d8 edi=7c99e3e0
eip=80542900 esp=f824addc ebp=0225f644 iopl=0         nv up ei ng nz na po cy
cs=0008  ss=0010  ds=0023  es=0023  fs=0030  gs=0000             efl=00000283
nt!KiSystemCallExit2+0x13:
80542900 0f35            sysexit

注意:EDX的值。
说明:nt!KiSystemCallExit很少被调用。

ntdll!KiFastSystemCallRet:
                           001b:7c92e514 c3              ret

操作步骤:
1.在nt!KiSystemCallExit2下断点。
2.走到sysexit时,在不发生切换线程的情况下会走到ntdll!KiFastSystemCallRet的。
3.ntdll!KiFastSystemCallRet返回到函数的调用者处。
------------------------------------------------------------------------------------------------------------------------------

made by correy
made at 2014.07.22

2014年7月21日星期一

内核函数的前缀

What Does the Zw Prefix Mean?
http://msdn.microsoft.com/en-us/library/windows/hardware/ff565646(v=vs.85).aspx

The Windows native system services routines have names that begin with the prefixes Nt

and Zw.
The Nt prefix is an abbreviation of Windows NT, but the Zw prefix has no meaning.
Zw was selected partly to avoid potential naming conflicts with other APIs, and partly to

avoid using any potentially useful two-letter prefixes that might be needed in the

future.

Many of the Windows driver support routines have names that begin with two- or three-

letter prefixes.
These prefixes indicate which kernel-mode system components implement the routines.
The following table contains some examples.

Prefix         Kernel component            Example routine
Cm             Configuration manager       CmRegisterCallbackEx
Ex             Executive                   ExAllocatePool
Hal            Hardware abstraction layer  HalGetAdapter
Io             I/O manager                 IoAllocateIrp
Ke             Kernel core                 KeSetEvent
Mm             Memory manager              MmUnlockPages
Ob             Object manager              ObReferenceObject
Po             Power manager               PoSetPowerState
Tm             Transaction manager         TmCommitTransaction
Nt and Zw      Native system services      NtCreateFile and ZwCreateFile

以上是原文。
总以为,相信这是比较全的。其实还有更多的前缀,现在给予补充:
cc             缓存管理。                  
DBG            调试管理。注意还有两个以v开头的。
FsRtl          File System Runtime Library Routines
Inbv           内核底层打印用的。
Interlocked    原子操作。
Kd             调试。
Ki             更加内核。
Ldr            PE文件资源相关的。
Lpc            进程间通讯。
Lsa            安全子系统相关。
Nls            语言相关。
Pfx        
ProbeFor       地址检测。
Ps             Process and Thread Manager Routines
Rtl            Runtime Library Routines
Se             Security Reference Monitor Routines
Ver        
Vf             可能是验证的。
WRITE_REGISTER 操作寄存器和端口相关。
Wmi            WMI Library Routines
_str或者_wcs等 内核中C运行时函数。
str或者wcs等   内核中C运行时函数。
Clfs           CLFS Library Routines
Flt            FltXxx (Minifilter Driver) Routines
Sec            Kernel Security Support Routines
MRx            Network Mini-Redirector Routines
Rx             Network Mini-Redirector Support Routines
Kf             在HAL.dll里面。


特殊的函数/变量:
GetSecurityUserInfo
MapSecurityError
HeadlessDispatch
initSafeBootMode
XipDispatch


二级前缀,或者说是隐含的前缀:
f    估计是fast的意思。
x    如Rtlx。
更多的还有:
p    估计是私有的。
i    估计是内部的。
更多的请看WDK。
                     

磁盘驱动,网络驱动等不再此收集之列。

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

持续更新中。

2014年7月16日星期三

minifilter之删除和改名操作

#include <fltKernel.h>
#include <windef.h>
#include <ntddk.h>

PFLT_FILTER gFilterHandle;

#define TAG 'tset' //test

/*
文件名:minifilter之删除和改名操作。

一般的用户的删除的操作的设置是移动到回收站。
也可以设置为不移动到回收站而直接删除。
选中文件再按SHITF+DELETE是直接删除而不是移动到回收站。
所以普通的删除操作要走两个地方:一是删除,而是改名。一个想法是能区分/识别这两个操作为一个操作不?

参考:
http://msdn.microsoft.com/en-us/library/windows/hardware/ff549366(v=vs.85).aspx
http://msdn.microsoft.com/en-us/library/windows/hardware/ff544789(v=vs.85).aspx
http://msdn.microsoft.com/en-us/library/windows/hardware/ff550799(v=vs.85).aspx

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


FLT_PREOP_CALLBACK_STATUS SetInformationPreOperation(__inout PFLT_CALLBACK_DATA Data, __in PCFLT_RELATED_OBJECTS FltObjects, __out PVOID *CompletionContext)
{
    PFLT_FILE_NAME_INFORMATION    pfni;
    NTSTATUS                      status;

    UNREFERENCED_PARAMETER( CompletionContext );  

    if(!NT_SUCCESS(Data->IoStatus.Status))
    {
        return FLT_PREOP_SUCCESS_WITH_CALLBACK;//The filter driver must pass this IRP down to the next-lower driver on the stack.
    } 

    /*
    FltGetFileNameInformation cannot get file name information if the TopLevelIrp field of the current thread is not NULL, 
    because the resulting file system recursion could cause deadlocks or stack overflows. (For more information about this issue, see IoGetTopLevelIrp.) 
    FltGetFileNameInformation cannot get file name information in the paging I/O path. 
    FltGetFileNameInformation cannot get file name information in the post-close path. 
    FltGetFileNameInformation cannot get the short name of a file in the pre-create path. 
    */
    if (FlagOn(Data->Iopb->IrpFlags, IRP_PAGING_IO) || FlagOn(Data->Iopb->IrpFlags, IRP_SYNCHRONOUS_PAGING_IO) || IoGetTopLevelIrp()) //IRP_NOCACHE
    {
        return FLT_PREOP_SUCCESS_WITH_CALLBACK;
    }

    status = FltGetFileNameInformation( Data, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT, &pfni);    
    if (!NT_SUCCESS( status )) 
    {
        return FLT_PREOP_SUCCESS_WITH_CALLBACK;
    }

    status = FltParseFileNameInformation(pfni);
    if (!NT_SUCCESS( status )) 
    {
        FltReleaseFileNameInformation(pfni); 
        return FLT_PREOP_SUCCESS_WITH_CALLBACK;
    }
    
    //FltReleaseFileNameInformation(pfni); 

    switch(Data->Iopb->Parameters.SetFileInformation.FileInformationClass)
    {  
    case FileDispositionInformation://删除。
        {
            PFILE_DISPOSITION_INFORMATION pfdi = (PFILE_DISPOSITION_INFORMATION)Data->Iopb->Parameters.SetFileInformation.InfoBuffer;

            /*
            Indicates whether the operating system file should delete the file when the file is closed.
            Set this member to TRUE to delete the file when it is closed.
            Otherwise, set to FALSE. 
            Setting this member to FALSE has no effect if the handle was opened with FILE_FLAG_DELETE_ON_CLOSE.

            严格的说还可以判断pfdi->DeleteFile的值。上面的是条件。
            */
            if (pfdi->DeleteFile == TRUE) 
            {
                KdPrint(("删除文件\n"));
            }

            /*
            #define FO_DELETE_ON_CLOSE              0x00010000
            FILE_FLAG_DELETE_ON_CLOSE 0x04000000    //http://msdn.microsoft.com/en-us/library/windows/desktop/aa363858(v=vs.85).aspx
            到底该用哪个呢?
            */
            if (FlagOn(Data->Iopb->TargetFileObject->Flags, FO_DELETE_ON_CLOSE))//FO_DELETE_ON_CLOSE OR FILE_FLAG_DELETE_ON_CLOSE?
            {
                KdPrint(("打开时就已经指定删除此文件了\n"));
            }  
            
            /*
            For rename or link operations. 
            If InfoBuffer->FileName contains a fully qualified file name, or if InfoBuffer->RootDirectory is non-NULL, 
            this member is a file object pointer for the parent directory of the file that is the target of the operation. 
            Otherwise it is NULL.

            还可以根据情况判断ParentOfTarget是否可用,然后再根据ParentOfTarget获取被删除文件的所在的目录。
            */

            /*
            A file marked for deletion is not actually deleted until all open handles for the file object have been closed and the link count for the file is zero.
            注意:文件不会立即删除。是延迟的。
            */

            KdPrint(("删除文件/目录:%wZ\n", &pfni->Name));
        }

        KdPrint(("\n"));
        break;
    case FileRenameInformation://改名。 
        {
            PFILE_RENAME_INFORMATION  pfri = (PFILE_RENAME_INFORMATION )Data->Iopb->Parameters.SetFileInformation.InfoBuffer;
            UNICODE_STRING new_name;

            /*
            注意有一些重命名的规则和注意事项,包括目录的,见msdn.
            */

            /*
            RootDirectory
            If the file is not being moved to a different directory, or if the FileName member contains the full pathname, this member is NULL. 
            Otherwise, it is a handle for the root directory under which the file will reside after it is renamed.

            pfri->RootDirectory不是判断改名和移动的标志,仅仅用于获取路径。因为FileName大多是全路径。
            */
            if (pfri->RootDirectory == NULL)
            {
                //本目录改名,FileName是全路径。
                //KdPrint(("改名!\n"));
            }
            else
            {
                /*
                就是移动,普通的删除(移动到回收站)。
                根据RootDirectory和FileName获取全路径。
                */
                //KdPrint(("移动!\n"));
            }

            /*
            关于原来的文件名字的获取的思路:
            1.Data->Iopb->TargetFileObject 或者 FltObjects->FileObject,其实这二者是相等的一样的。
              其实这个值是和pfni->Name一致的。
            2.Data->Iopb->TargetFileObject->FsContext和Data->Iopb->TargetFileObject->FsContext2。
            不过思路2有很多未公开的结构,ccb,fcb等,可参考:
            http://dokan.googlecode.com/svn/trunk/sys/dokan.h
            http://dokan.googlecode.com/svn/trunk/sys/fileinfo.c
            */
            //KdPrint(("改名/移动前的文件/目录:%wZ\n", &Data->Iopb->TargetFileObject->FileName));
            KdPrint(("改名/移动前的文件/目录:%wZ\n", &pfni->Name));

            /*
            FileName是改名后的路径,和pfni->Name的值在此时是不一样的。
            */
            new_name.Buffer = pfri->FileName;
            new_name.Length = (USHORT)pfri->FileNameLength;
            new_name.MaximumLength = new_name.Length;
            KdPrint(("改名/移动后的文件/目录:%wZ\n", &new_name));
        }

        KdPrint(("\n"));
        break;
    default://其实还有更多,不信你看头文件,注意对应的结构的处理。
        KdPrint(("设置信息的类型:%d\n\n", Data->Iopb->Parameters.SetFileInformation.FileInformationClass));
        break;
    }   

    FltReleaseFileNameInformation(pfni); 
   
    return FLT_PREOP_SUCCESS_WITH_CALLBACK;
}


FLT_POSTOP_CALLBACK_STATUS SetInformationPostOperation (__inout PFLT_CALLBACK_DATA Data, __in PCFLT_RELATED_OBJECTS FltObjects, __in_opt PVOID CompletionContext, __in FLT_POST_OPERATION_FLAGS Flags)
    /*
    这个无用,暂时不用。
    */
{
    PFLT_FILE_NAME_INFORMATION    pfni;
    NTSTATUS                      status;

    UNREFERENCED_PARAMETER( CompletionContext );  

    if(!NT_SUCCESS(Data->IoStatus.Status))
    {
        return FLT_POSTOP_FINISHED_PROCESSING;//The filter driver must pass this IRP down to the next-lower driver on the stack.
    } 

    /*
    FltGetFileNameInformation cannot get file name information if the TopLevelIrp field of the current thread is not NULL, 
    because the resulting file system recursion could cause deadlocks or stack overflows. (For more information about this issue, see IoGetTopLevelIrp.) 
    FltGetFileNameInformation cannot get file name information in the paging I/O path. 
    FltGetFileNameInformation cannot get file name information in the post-close path. 
    FltGetFileNameInformation cannot get the short name of a file in the pre-create path. 
    */
    if (FlagOn(Data->Iopb->IrpFlags, IRP_PAGING_IO) || FlagOn(Data->Iopb->IrpFlags, IRP_SYNCHRONOUS_PAGING_IO) || IoGetTopLevelIrp()) //IRP_NOCACHE
    {
        return FLT_POSTOP_FINISHED_PROCESSING;
    }

    status = FltGetFileNameInformation( Data, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT, &pfni);    
    if (!NT_SUCCESS( status )) 
    {
        return FLT_POSTOP_FINISHED_PROCESSING;
    }

    status = FltParseFileNameInformation(pfni);
    if (!NT_SUCCESS( status )) 
    {
        FltReleaseFileNameInformation(pfni); 
        return FLT_POSTOP_FINISHED_PROCESSING;
    }
    
    //FltReleaseFileNameInformation(pfni); 

    switch(Data->Iopb->Parameters.SetFileInformation.FileInformationClass)
    {  
    case FileDispositionInformation://删除。
        {
            PFILE_DISPOSITION_INFORMATION pfdi = (PFILE_DISPOSITION_INFORMATION)Data->Iopb->Parameters.SetFileInformation.InfoBuffer;

            /*
            Indicates whether the operating system file should delete the file when the file is closed.
            Set this member to TRUE to delete the file when it is closed.
            Otherwise, set to FALSE. 
            Setting this member to FALSE has no effect if the handle was opened with FILE_FLAG_DELETE_ON_CLOSE.

            严格的说还可以判断pfdi->DeleteFile的值。上面的是条件。
            */
            if (pfdi->DeleteFile == TRUE) 
            {
                //KdPrint(("删除文件\n"));
            }

            /*
            #define FO_DELETE_ON_CLOSE              0x00010000
            FILE_FLAG_DELETE_ON_CLOSE 0x04000000    //http://msdn.microsoft.com/en-us/library/windows/desktop/aa363858(v=vs.85).aspx
            到底该用哪个呢?
            */
            if (FlagOn(Data->Iopb->TargetFileObject->Flags, FO_DELETE_ON_CLOSE))//FO_DELETE_ON_CLOSE OR FILE_FLAG_DELETE_ON_CLOSE?
            {
                //KdPrint(("打开时就已经指定删除此文件了\n"));
            }  
            
            /*
            For rename or link operations. 
            If InfoBuffer->FileName contains a fully qualified file name, or if InfoBuffer->RootDirectory is non-NULL, 
            this member is a file object pointer for the parent directory of the file that is the target of the operation. 
            Otherwise it is NULL.

            还可以根据情况判断ParentOfTarget是否可用,然后再根据ParentOfTarget获取被删除文件的所在的目录。
            */

            /*
            A file marked for deletion is not actually deleted until all open handles for the file object have been closed and the link count for the file is zero.
            注意:文件不会立即删除。是延迟的。
            */

            //KdPrint(("删除文件/目录:%wZ\n", &pfni->Name));
        }

        //KdPrint(("\n"));
        break;
    case FileRenameInformation://改名。 
        {
            PFILE_RENAME_INFORMATION  pfri = (PFILE_RENAME_INFORMATION )Data->Iopb->Parameters.SetFileInformation.InfoBuffer;

            /*
            注意有一些重命名的规则和注意事项,包括目录的,见msdn.
            */

            /*
            RootDirectory
            If the file is not being moved to a different directory, or if the FileName member contains the full pathname, this member is NULL. 
            Otherwise, it is a handle for the root directory under which the file will reside after it is renamed.

            pfri->RootDirectory不是判断改名和移动的标志,仅仅用于获取路径。因为FileName大多是全路径。
            */
            if (pfri->RootDirectory == NULL)
            {
                //本目录改名,FileName是全路径。
                //KdPrint(("改名!\n"));
            }
            else
            {
                /*
                就是移动,普通的删除(移动到回收站)。
                根据RootDirectory和FileName获取全路径。
                */
                //KdPrint(("移动!\n"));
            }

            /*
            关于原来的文件名字的获取的思路:
            1.Data->Iopb->TargetFileObject 或者 FltObjects->FileObject,其实这二者是相等的一样的。其实这个值是和pfni->Name一致的。
              这个有时是改名后的路径有时是改名前的路径。此办法不可信,不可用。还得组合路径是必须的。
            2.Data->Iopb->TargetFileObject->FsContext和Data->Iopb->TargetFileObject->FsContext2。
            不过思路2有很多未公开的结构,ccb,fcb等,可参考:
            http://dokan.googlecode.com/svn/trunk/sys/dokan.h
            http://dokan.googlecode.com/svn/trunk/sys/fileinfo.c

            获取改名前的名字在和路径在前操作中进行。
            */
            //KdPrint(("改名/移动前的文件/目录:%wZ\n", &Data->Iopb->TargetFileObject->FileName));

            /*
            FileName是改名后的路径,和pfni->Name是一样的。\Device\HarddiskVolume1\和\??\之别。
            */
            //KdPrint(("改名/移动后的文件/目录:%wZ\n", &pfni->Name));
        }

        //KdPrint(("\n"));
        break;
    default://其实还有更多,不信你看头文件,注意对应的结构的处理。
        //KdPrint(("设置信息的类型:%d\n\n", Data->Iopb->Parameters.SetFileInformation.FileInformationClass));
        break;
    }   

    FltReleaseFileNameInformation(pfni); 

    return FLT_POSTOP_FINISHED_PROCESSING;
}


CONST FLT_OPERATION_REGISTRATION Callbacks[] = {
    { IRP_MJ_SET_INFORMATION,  0, SetInformationPreOperation, SetInformationPostOperation},
    { IRP_MJ_OPERATION_END }
};


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


#pragma PAGEDCODE//#pragma alloc_text(PAGE, PtUnload)
NTSTATUS MiniFilterUnload (__in FLT_FILTER_UNLOAD_FLAGS Flags)
{
    FltUnregisterFilter( gFilterHandle );
    return STATUS_SUCCESS;
}


CONST FLT_REGISTRATION FilterRegistration = {
    sizeof( FLT_REGISTRATION ),         //  Size
    FLT_REGISTRATION_VERSION,           //  Version
    0,                                  //  Flags
    NULL,                               //  Context
    Callbacks,                          //  Operation callbacks
    MiniFilterUnload,                   //  MiniFilterUnload
    NULL,                               //  InstanceSetup
    InstanceQueryTeardown,              //  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;

    UNREFERENCED_PARAMETER( RegistryPath );

    KdBreakPoint();

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

    status = FltStartFiltering( gFilterHandle );
    if (!NT_SUCCESS( status )) {
        FltUnregisterFilter( gFilterHandle );
    }

    return status;
}

2014年7月11日星期五

内核中的通配符和字符匹配

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


/*
FsRtlIsNameInExpression的用法规则:
1.If IgnoreCase is TRUE, Expression must be uppercase.
  如果忽略大小写(第三个参数),第一个参数必须是大写的。
2.If this value is not supplied, the default system uppercase character table is used.
  如果第四个参数为0,那(第一个参数)必须是大写的。
3.If only one of the string parameters has a length of zero, FsRtlIsNameInExpression returns FALSE. 
  This means that "*" does not match a null string. 
  如果有一个参数是0,返回失败。
4.If both parameters are null strings, FsRtlIsNameInExpression returns TRUE.
  如果都是0,返回匹配。

Wildcard character Meaning 
* (asterisk)       Matches zero or more characters. 
? (question mark)  Matches a single character. 
DOS_DOT            Matches either a period or zero characters beyond the name string. 
DOS_QM             Matches any single character or, upon encountering a period or end of name string, advances the expression to the end of the set of contiguous DOS_QMs. 
DOS_STAR           Matches zero or more characters until encountering and matching the final . in the name. 

DOS_QM和DOS_STAR是啥东西呢?

#define DOS_STAR        (L'<')
#define DOS_QM          (L'>')
#define DOS_DOT         (L'"') //dot也就是L".",为何定义这呢?不过看源码,它也处理了L"."
摘自:\wrk\WindowsResearchKernel-WRK\WRK-v1.2\public\sdk\inc\ntioapi.h

但是另一个文档的说明如下:
~* is DOS_STAR, ~? is DOS_QM, and ~. is DOS_DOT
摘自:\wrk\WindowsResearchKernel-WRK\WRK-v1.2\base\ntos\fsrtl\name.c

前后不符。

综上所述:DOS_QM和DOS_STAR,DOS_DOT是处理DOS路径的(个人理解)。

参考资料:MSDN和WRK。

多字符的FsRtlIsDbcsInExpression与此类似。

不说了,能用则已,简单的几个,不够用的时候在仔细看。
  
made by correy
made at 2014.07.11
homepage:http://correy.webs.com
*/


BOOLEAN RtlIsNameInExpression(IN UNICODE_STRING * Expression, IN UNICODE_STRING * Name)
{
    NTSTATUS status = STATUS_UNSUCCESSFUL;
    UNICODE_STRING  DestinationString;
    BOOLEAN B;

    if (Expression == 0 || Name == 0)
    {
        return FALSE ;
    }

    status = RtlUpcaseUnicodeString(&DestinationString, Expression, TRUE);
    if (!NT_SUCCESS( status )) 
    {
        return FALSE ;
    }

    B = FsRtlIsNameInExpression(&DestinationString, Name, TRUE, 0);

    RtlFreeUnicodeString(&DestinationString);

    return B;
}


DRIVER_INITIALIZE DriverEntry;
NTSTATUS DriverEntry( __in struct _DRIVER_OBJECT  * DriverObject, __in PUNICODE_STRING  RegistryPath)
{
    NTSTATUS status = STATUS_UNSUCCESSFUL; 
    UNICODE_STRING Expression1 = RTL_CONSTANT_STRING(L"co*ey");
    UNICODE_STRING Expression2 = RTL_CONSTANT_STRING(L"cor?ey");
    UNICODE_STRING Name  = RTL_CONSTANT_STRING(L"correy");
    UNICODE_STRING Name2  = RTL_CONSTANT_STRING(L"coeyrr");
    
    KdBreakPoint();

    if (RtlIsNameInExpression(&Expression1, &Name))
    {
        KdPrint(("匹配\n"));
    }
    else
    {
        KdPrint(("不匹配\n"));
    }

    if (RtlIsNameInExpression(&Expression2, &Name))
    {
        KdPrint(("匹配\n"));
    }
    else
    {
        KdPrint(("不匹配\n"));
    }    

    //以上都因该匹配。
    /////////////////////////////////////////////////////
    //以下都应该不匹配。

    if (RtlIsNameInExpression(&Expression1, &Name2))
    {
        KdPrint(("匹配\n"));
    }
    else
    {
        KdPrint(("不匹配\n"));
    }

    if (RtlIsNameInExpression(&Expression2, &Name2))
    {
        KdPrint(("匹配\n"));
    }
    else
    {
        KdPrint(("不匹配\n"));
    }    

    return status;//STATUS_SUCCESS
} 

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

/*
通配符的测试。

The following are wildcard characters: *, ?, ANSI_DOS_STAR, ANSI_DOS_DOT, and ANSI_DOS_QM.

FsRtlDoesNameContainWildCards 是个函数,调用FsRtlIsUnicodeCharacterWild实现的。
FsRtlIsUnicodeCharacterWild 是个宏
FsRtlIsAnsiCharacterWild 是个宏

made by correy
made at 2015.03.04

#define DOS_STAR        (L'<')
#define DOS_QM          (L'>')
#define DOS_DOT         (L'"') //dot也就是L".",为何定义这呢?不过看源码,它也处理了L"."
摘自:\wrk\WindowsResearchKernel-WRK\WRK-v1.2\public\sdk\inc\ntioapi.h

但是另一个文档的说明如下:
~* is DOS_STAR, ~? is DOS_QM, and ~. is DOS_DOT
摘自:\wrk\WindowsResearchKernel-WRK\WRK-v1.2\base\ntos\fsrtl\name.c
经测试:这个是不正确的,不可信的。

其实:
#define ANSI_DOS_STAR   ('<') 但是这代表啥意思的呢?还没有测试。
#define ANSI_DOS_QM     ('>')
#define ANSI_DOS_DOT    ('"')

#define DOS_STAR        (L'<')
#define DOS_QM          (L'>')
#define DOS_DOT         (L'"')
这些都定义在ntifs.h中的。
*/

#include <ntifs.h>
//#include <ntddk.h> //这两个次序不能乱,有上面的,这个可以注释掉。

#pragma INITCODE
DRIVER_INITIALIZE DriverEntry;
NTSTATUS DriverEntry( __in struct _DRIVER_OBJECT  * DriverObject, __in PUNICODE_STRING  RegistryPath)
{
    NTSTATUS status = STATUS_UNSUCCESSFUL;
    BOOLEAN B = FALSE;

    UNICODE_STRING test1 = RTL_CONSTANT_STRING(L"*");
    UNICODE_STRING test2 = RTL_CONSTANT_STRING(L"a*");
    UNICODE_STRING test3 = RTL_CONSTANT_STRING(L"*a");
    UNICODE_STRING test4 = RTL_CONSTANT_STRING(L"*a*");

    UNICODE_STRING test5 = RTL_CONSTANT_STRING(L"?");
    UNICODE_STRING test6 = RTL_CONSTANT_STRING(L"a?");
    UNICODE_STRING test7 = RTL_CONSTANT_STRING(L"?a");
    UNICODE_STRING test8 = RTL_CONSTANT_STRING(L"?a?");

    UNICODE_STRING test9 = RTL_CONSTANT_STRING(L"test");

    UNICODE_STRING test10 = RTL_CONSTANT_STRING(L"<");
    UNICODE_STRING test11 = RTL_CONSTANT_STRING(L">");
    UNICODE_STRING test12 = RTL_CONSTANT_STRING(L"\"");
    UNICODE_STRING test13 = RTL_CONSTANT_STRING(L".");

    //混合的就不测试了,因为单个的测试了,混合的必定可以的。

    KdBreakPoint();
      
    B = FsRtlDoesNameContainWildCards(&test1);
    if (B) {
        KdPrint(("%wZ contains wildcard characters!\n", &test1));
    } else {
        KdPrint(("%wZ not contains wildcard characters!\n", &test1));
    }

    B = FsRtlDoesNameContainWildCards(&test2);
    if (B) {
        KdPrint(("%wZ contains wildcard characters!\n", &test2));
    } else {
        KdPrint(("%wZ not contains wildcard characters!\n", &test2));
    }

    B = FsRtlDoesNameContainWildCards(&test3);
    if (B) {
        KdPrint(("%wZ contains wildcard characters!\n", &test3));
    } else {
        KdPrint(("%wZ not contains wildcard characters!\n", &test3));
    }

    B = FsRtlDoesNameContainWildCards(&test4);
    if (B) {
        KdPrint(("%wZ contains wildcard characters!\n", &test4));
    } else {
        KdPrint(("%wZ not contains wildcard characters!\n", &test4));
    }

    B = FsRtlDoesNameContainWildCards(&test5);
    if (B) {
        KdPrint(("%wZ contains wildcard characters!\n", &test5));
    } else {
        KdPrint(("%wZ not contains wildcard characters!\n", &test5));
    }

    B = FsRtlDoesNameContainWildCards(&test6);
    if (B) {
        KdPrint(("%wZ contains wildcard characters!\n", &test6));
    } else {
        KdPrint(("%wZ not contains wildcard characters!\n", &test6));
    }

    B = FsRtlDoesNameContainWildCards(&test7);
    if (B) {
        KdPrint(("%wZ contains wildcard characters!\n", &test7));
    } else {
        KdPrint(("%wZ not contains wildcard characters!\n", &test7));
    }

    B = FsRtlDoesNameContainWildCards(&test8);
    if (B) {
        KdPrint(("%wZ contains wildcard characters!\n", &test8));
    } else {
        KdPrint(("%wZ not contains wildcard characters!\n", &test8));
    }

    B = FsRtlDoesNameContainWildCards(&test9);
    if (B) {
        KdPrint(("%wZ contains wildcard characters!\n", &test9));
    } else {
        KdPrint(("%wZ not contains wildcard characters!\n", &test9));
    }

    B = FsRtlDoesNameContainWildCards(&test10);
    if (B) {
        KdPrint(("%wZ contains wildcard characters!\n", &test10));
    } else {
        KdPrint(("%wZ not contains wildcard characters!\n", &test10));
    }

    B = FsRtlDoesNameContainWildCards(&test11);
    if (B) {
        KdPrint(("%wZ contains wildcard characters!\n", &test11));
    } else {
        KdPrint(("%wZ not contains wildcard characters!\n", &test11));
    }

    B = FsRtlDoesNameContainWildCards(&test12);
    if (B) {
        KdPrint(("%wZ contains wildcard characters!\n", &test12));
    } else {
        KdPrint(("%wZ not contains wildcard characters!\n", &test12));
    }

    B = FsRtlDoesNameContainWildCards(&test13);
    if (B) {
        KdPrint(("%wZ contains wildcard characters!\n", &test13));
    } else {
        KdPrint(("%wZ not contains wildcard characters!\n", &test13));
    }
      
    return status;//STATUS_SUCCESS
}


/*
测试结果如下:
1: kd> g
* contains wildcard characters!
a* contains wildcard characters!
*a contains wildcard characters!
*a* contains wildcard characters!
? contains wildcard characters!
a? contains wildcard characters!
?a contains wildcard characters!
?a? contains wildcard characters!
test not contains wildcard characters!
< contains wildcard characters!
> contains wildcard characters!
" contains wildcard characters!
. not contains wildcard characters!
*/

2014年7月10日星期四

minifilter打开/创建的前操作识别目录

#include <fltKernel.h>

PFLT_FILTER gFilterHandle;

/*
打开/创建的前操作的时候判断是不是目录。

打开/创建的前操作不可以用FltIsDirectory,因为第一个参数为空/不可以使用。

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

//这些在WDK 7600.16385.1中没有定义,在WDK8.0中定义了.
//一下代码是解决办法之一.
#ifndef _In_
#define _Inout_
#define _In_
#define _In_opt_
#endif
//另一思路可参考:http://msdn.microsoft.com/zh-cn/library/windows/hardware/ff554695(v=vs.85).aspx


FLT_PREOP_CALLBACK_STATUS CreatePreOPeration(__inout PFLT_CALLBACK_DATA Cbd, __in PCFLT_RELATED_OBJECTS FltObjects, __out PVOID *CompletionContext)
{
    PFLT_FILE_NAME_INFORMATION    pfni;
    NTSTATUS                      status;
    FILE_STANDARD_INFORMATION fsi = {0};
    ULONG  LengthReturned;
    BOOLEAN  IsDirectory = FALSE ;

    //status = FltIsDirectory(FltObjects->FileObject, FltObjects->Instance, &IsDirectory);//FileObject不可用,所以这个函数不可用。
    //if (NT_SUCCESS(status) && IsDirectory) 
    //{  
    //    //如果是目录.
    //}
    //else
    //{
    //    return FLT_PREOP_SUCCESS_NO_CALLBACK;
    //}

    /*
    方法一:
    确信用户打开文件填写的是FILE_NON_DIRECTORY_FILE,打开目录是FILE_DIRECTORY_FILE。
    应用程序没有这个选项,所以过滤应用层是正确的。驱动层的有的程序会填写错误。
    */
    if (!FlagOn( Cbd->Iopb->Parameters.Create.Options, FILE_DIRECTORY_FILE ))
    {
        return FLT_PREOP_SUCCESS_NO_CALLBACK;
    }

    /*
    创建的标志:
    FILE_SUPERSEDE 
    FILE_CREATE 
    FILE_OPEN_IF 
    FILE_OVERWRITE_IF

    打开标志:
    FILE_OPEN  
    FILE_OPEN_IF  
    FILE_OVERWRITE  
    FILE_OVERWRITE_IF 
    注释:FILE_OPEN和FILE_OVERWRITE不会创建。    
    */  

    /*
    方法二:FltQueryInformationFile OR ZwQueryInformationFile。
    最好判断下目的。新建的估计失败,估计是PFILE_OBJECT不存在。
    */
    //status = FltQueryInformationFile(FltObjects->Instance, FltObjects->FileObject, &fsi, sizeof(FILE_STANDARD_INFORMATION), FileStandardInformation, &LengthReturned);
    //if (!NT_SUCCESS( status )) 
    //{
    //    return FLT_PREOP_SUCCESS_NO_CALLBACK;
    //}
    //if (fsi.Directory != TRUE )
    //{
    //    return FLT_PREOP_SUCCESS_NO_CALLBACK;
    //}

    /*
    FltGetFileNameInformation cannot get file name information if the TopLevelIrp field of the current thread is not NULL, 
    because the resulting file system recursion could cause deadlocks or stack overflows. (For more information about this issue, see IoGetTopLevelIrp.) 
    FltGetFileNameInformation cannot get file name information in the paging I/O path. 
    FltGetFileNameInformation cannot get file name information in the post-close path. 
    FltGetFileNameInformation cannot get the short name of a file in the pre-create path. 
    */
    if (FlagOn(Cbd->Iopb->IrpFlags, IRP_PAGING_IO) || FlagOn(Cbd->Iopb->IrpFlags, IRP_SYNCHRONOUS_PAGING_IO) || IoGetTopLevelIrp()) //IRP_NOCACHE
    {
        return FLT_PREOP_SUCCESS_NO_CALLBACK;
    }
    
    status = FltGetFileNameInformation( Cbd, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT, &pfni);    
    if (!NT_SUCCESS( status )) 
    {
        return FLT_PREOP_SUCCESS_NO_CALLBACK;
    }

    status = FltParseFileNameInformation(pfni);
    if (!NT_SUCCESS( status )) 
    {
        FltReleaseFileNameInformation(pfni); 
        return FLT_PREOP_SUCCESS_NO_CALLBACK;
    }

    KdPrint(("目录:%wZ\n", &pfni->Name));

    FltReleaseFileNameInformation(pfni);

    return FLT_PREOP_SUCCESS_NO_CALLBACK;
}


CONST FLT_OPERATION_REGISTRATION Callbacks[] = {
    { IRP_MJ_CREATE,  0, CreatePreOPeration, 0},
    { IRP_MJ_OPERATION_END }
};


#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)
{
    FltUnregisterFilter( gFilterHandle );
    return STATUS_SUCCESS;
}


CONST FLT_REGISTRATION FilterRegistration = {
    sizeof( FLT_REGISTRATION ),         //  Size
    FLT_REGISTRATION_VERSION,           //  Version
    0,                                  //  Flags
    NULL,                               //  Context
    Callbacks,                          //  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;

    UNREFERENCED_PARAMETER( RegistryPath );

    KdBreakPoint();

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

    status = FltStartFiltering( gFilterHandle );
    if (!NT_SUCCESS( status )) {
        FltUnregisterFilter( gFilterHandle );
    }

    return status;
}

FltDoCompletionProcessingWhenSafe

#include <fltKernel.h>

PFLT_FILTER gFilterHandle;

/*
文件名:FltDoCompletionProcessingWhenSafe.c 

起因在写的后操作直接调用FltGetFileNameInformation蓝屏。
后来得知是后操作的IRQL <= DISPATCH_LEVEL。
解决办法有2:
1.FltDoCompletionProcessingWhenSafe
2.FltQueueDeferredIoWorkItem(自己试验失败,总是蓝屏)
3.个人不建议使用普通的线程队列,因为有些内核对象不能使用。还是用官方建议的吧!

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

//这些在WDK 7600.16385.1中没有定义,在WDK8.0中定义了.
//一下代码是解决办法之一.
#ifndef _In_
#define _Inout_
#define _In_
#define _In_opt_
#endif
//另一思路可参考:http://msdn.microsoft.com/zh-cn/library/windows/hardware/ff554695(v=vs.85).aspx


FLT_POSTOP_CALLBACK_STATUS WritePostOPerationWhenSafe (__inout PFLT_CALLBACK_DATA Data, __in PCFLT_RELATED_OBJECTS FltObjects, __in_opt PVOID CompletionContext, __in FLT_POST_OPERATION_FLAGS Flags)
{
    PFLT_FILE_NAME_INFORMATION    pfni;
    NTSTATUS                      status;

    status = FltGetFileNameInformation( Data, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT, &pfni);    
    if (!NT_SUCCESS( status )) 
    {
        return FLT_POSTOP_FINISHED_PROCESSING;
    }

    status = FltParseFileNameInformation(pfni);
    if (!NT_SUCCESS( status )) 
    {
        FltReleaseFileNameInformation(pfni); 
        return FLT_POSTOP_FINISHED_PROCESSING;
    }

    KdPrint(("写文件:%wZ\n", &pfni->Name));

    FltReleaseFileNameInformation(pfni); 

    return FLT_POSTOP_FINISHED_PROCESSING;

    /*
    If a minifilter calls FltDoCompletionProcessingWhenSafe and the SafePostCallback is invoked in a worker thread because it is not safe to invoke it in the current thread context, 
    the filter manager will resume completion processing as long as the minifilter does not return FLT_POSTOP_MORE_PROCESSING_REQUIRED from the SafePostCallback. 

    If the minifilter does return FLT_POSTOP_MORE_PROCESSING_REQUIRED from the SafePostCallback, 
    the minifilter must call FltCompletePendedPostOperation to resume completion processing.
    理解为,如果这个函数返回了FLT_POSTOP_MORE_PROCESSING_REQUIRED,后操作要调用FltCompletePendedPostOperation。
    */
}


FLT_POSTOP_CALLBACK_STATUS WritePostOPeration(__inout PFLT_CALLBACK_DATA Data, __in PCFLT_RELATED_OBJECTS FltObjects, __in PVOID CompletionContext, __in FLT_POST_OPERATION_FLAGS Flags)
    /*
    A minifilter driver's post-operation callback routine performs completion processing for one or more types of I/O operations.
    Post-operation callback routines are similar to the completion routines used by legacy file system filter drivers. 
    Post-operation callback routines are called in an arbitrary thread context, at IRQL <= DISPATCH_LEVEL. 
    Because this callback routine can be called at IRQL DISPATCH_LEVEL, it is subject to the following constraints: 

    It cannot safely call any kernel-mode routine that must run at a lower IRQL. 
    Any data structures used in this routine must be allocated from nonpaged pool. 
    It cannot be made pageable. 
    It cannot acquire resources, mutexes, or fast mutexes. However, it can acquire spin locks. 
    It cannot get, set, or delete contexts, but it can release contexts. 

    Any I/O completion processing that needs to be performed at IRQL < DISPATCH_LEVEL cannot be performed directly in the postoperation callback routine. 
    Instead, it must be posted to a work queue by calling a routine such as FltDoCompletionProcessingWhenSafe or FltQueueDeferredIoWorkItem. 

    Be aware that FltDoCompletionProcessingWhenSafe should never be called if the Flags parameter of the post-operation callback has the FLTFL_POST_OPERATION_DRAINING bit set. 
    The following are exceptions to this rule: 

    If a minifilter driver's pre-operation callback routine returns FLT_PREOP_SYNCHRONIZE for an IRP-based I/O operation, 
    the corresponding post-operation callback routine is guaranteed to be called at IRQL <= APC_LEVEL, in the same thread context as the pre-operation callback. 

    Post-create callback routines are guaranteed to be called at IRQL PASSIVE_LEVEL, in the context of the thread that originated the IRP_MJ_CREATE operation. 

    */
{
    FLT_POSTOP_CALLBACK_STATUS retValue = FLT_POSTOP_FINISHED_PROCESSING;

    /*
    FltDoCompletionProcessingWhenSafe can only be called for IRP-based operations. 
    To determine whether the operation is an IRP-based operation, use the FLT_IS_IRP_OPERATION macro.
    */
    if (!FLT_IS_IRP_OPERATION(Data)) //FlagOn( (Data)->Flags, FLTFL_CALLBACK_DATA_IRP_OPERATION )
    {
        return FLT_POSTOP_FINISHED_PROCESSING;
    }

    /*
    Note that FltDoCompletionProcessingWhenSafe should never be called if the Flags parameter of the postoperation callback has the FLTFL_POST_OPERATION_DRAINING bit set. 
    */
    if (FlagOn(Flags,FLTFL_POST_OPERATION_DRAINING))
    {
        return FLT_POSTOP_FINISHED_PROCESSING;
    }

    /*
    FltDoCompletionProcessingWhenSafe cannot be used to post completion of a paging I/O operation to a worker thread.
    */
    if (FlagOn(Data->Iopb->IrpFlags, IRP_PAGING_IO) || FlagOn(Data->Iopb->IrpFlags, IRP_SYNCHRONOUS_PAGING_IO)) //IRP_NOCACHE
    {
        return FLT_POSTOP_FINISHED_PROCESSING;
    }

    /*
    FltDoCompletionProcessingWhenSafe can only be called from a minifilter driver's postoperation callback routine (PFLT_POST_OPERATION_CALLBACK).   
    */
    if (!FltDoCompletionProcessingWhenSafe( Data, FltObjects, CompletionContext, Flags, WritePostOPerationWhenSafe, &retValue ))
    {
        Data->IoStatus.Status = STATUS_UNSUCCESSFUL;
        Data->IoStatus.Information = 0;
        return retValue;
    }

    //FltCompletePendedPostOperation();//如果返回了FLT_POSTOP_MORE_PROCESSING_REQUIRED就调用这个。

    return retValue;
}


CONST FLT_OPERATION_REGISTRATION Callbacks[] = {
    { IRP_MJ_WRITE,  0, 0, WritePostOPeration},
    { IRP_MJ_OPERATION_END }
};


#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)
{
    FltUnregisterFilter( gFilterHandle );
    return STATUS_SUCCESS;
}


CONST FLT_REGISTRATION FilterRegistration = {
    sizeof( FLT_REGISTRATION ),         //  Size
    FLT_REGISTRATION_VERSION,           //  Version
    0,                                  //  Flags
    NULL,                               //  Context
    Callbacks,                          //  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;

    UNREFERENCED_PARAMETER( RegistryPath );

    KdBreakPoint();

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

    status = FltStartFiltering( gFilterHandle );
    if (!NT_SUCCESS( status )) {
        FltUnregisterFilter( gFilterHandle );
    }

    return status;
}

minifilter-DirectoryControl-QueryDirectory

#include <fltKernel.h>
#include <windef.h>

PFLT_FILTER gFilterHandle;

#define TAG 'tset' //test

/*
文件名就叫:minifilter-QueryDirectory.c吧!
框架是DirectoryControl,主要写的是QueryDirectory,NotifyDirectory与此类似。

参考:
http://msdn.microsoft.com/en-us/library/windows/hardware/ff548658(v=vs.85).aspx
http://msdn.microsoft.com/en-us/library/windows/hardware/ff544695(v=vs.85).aspx
http://msdn.microsoft.com/en-us/library/windows/hardware/ff540314(v=vs.85).aspx

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

FLT_POSTOP_CALLBACK_STATUS DirectoryControlPostOperation (__inout PFLT_CALLBACK_DATA Data, __in PCFLT_RELATED_OBJECTS FltObjects, __in_opt PVOID CompletionContext, __in FLT_POST_OPERATION_FLAGS Flags)
{
    PVOID p = NULL;  
    PFLT_FILE_NAME_INFORMATION    pfni;
    NTSTATUS                      status;

    UNREFERENCED_PARAMETER( CompletionContext );  

    //IRP_MJ_DIRECTORY_CONTROL is an IRP-based operation.
    if (!FLT_IS_IRP_OPERATION(Data))
    {
        return FLT_POSTOP_FINISHED_PROCESSING;
    }

    /*
    The file system driver should check the minor function code to determine which directory control operation is requested. 

    One of the following:
    IRP_MN_NOTIFY_CHANGE_DIRECTORY 对应NotifyDirectory结构。
    IRP_MN_QUERY_DIRECTORY 对应QueryDirectory结构即(Union component used for IRP_MN_QUERY_DIRECTORY operations.)
    */
    if (Data->Iopb->MinorFunction == IRP_MN_NOTIFY_CHANGE_DIRECTORY)
    {
        return FLT_POSTOP_FINISHED_PROCESSING;//The filter driver must pass this IRP down to the next-lower driver on the stack.
    }

    if( FlagOn( Flags, FLTFL_POST_OPERATION_DRAINING ))
    {
        return FLT_POSTOP_FINISHED_PROCESSING;
    }

    if(Data->Iopb->Parameters.DirectoryControl.QueryDirectory.Length <= 0)
    {
        return FLT_POSTOP_FINISHED_PROCESSING;
    }

    if(!NT_SUCCESS(Data->IoStatus.Status))
    {
        return FLT_POSTOP_FINISHED_PROCESSING;
    }
    
    if (Data->Iopb->Parameters.DirectoryControl.QueryDirectory.MdlAddress != NULL) {
        p = MmGetSystemAddressForMdlSafe(Data->Iopb->Parameters.DirectoryControl.QueryDirectory.MdlAddress, NormalPagePriority);            
    }  else {
        p = Data->Iopb->Parameters.DirectoryControl.QueryDirectory.DirectoryBuffer;             
    } 
    if(p == NULL) {
        return FLT_POSTOP_FINISHED_PROCESSING;  
    }

    //这个不是目录。
    //KdPrint(("查询的目录:%wZ\n", Data->Iopb->Parameters.DirectoryControl.QueryDirectory.FileName));

    /*
    FltGetFileNameInformation cannot get file name information if the TopLevelIrp field of the current thread is not NULL, 
    because the resulting file system recursion could cause deadlocks or stack overflows. (For more information about this issue, see IoGetTopLevelIrp.) 
    FltGetFileNameInformation cannot get file name information in the paging I/O path. 
    FltGetFileNameInformation cannot get file name information in the post-close path. 
    FltGetFileNameInformation cannot get the short name of a file in the pre-create path. 
    */
    if (FlagOn(Data->Iopb->IrpFlags, IRP_PAGING_IO) || FlagOn(Data->Iopb->IrpFlags, IRP_SYNCHRONOUS_PAGING_IO) || IoGetTopLevelIrp()) //IRP_NOCACHE
    {
        return FLT_POSTOP_FINISHED_PROCESSING;
    }

    status = FltGetFileNameInformation( Data, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT, &pfni);    
    if (!NT_SUCCESS( status )) 
    {
        return FLT_POSTOP_FINISHED_PROCESSING;
    }

    status = FltParseFileNameInformation(pfni);
    if (!NT_SUCCESS( status )) 
    {
        FltReleaseFileNameInformation(pfni); 
        return FLT_POSTOP_FINISHED_PROCESSING;
    }

    KdPrint(("查询的目录:%wZ\n", &pfni->Name));

    //FltReleaseFileNameInformation(pfni); 

    switch(Data->Iopb->Parameters.DirectoryControl.QueryDirectory.FileInformationClass)
    {  
    case FileBothDirectoryInformation://XP走这里了,server 2008也走这里了。
        {
            PFILE_BOTH_DIR_INFORMATION pfbdi = (PFILE_BOTH_DIR_INFORMATION)p;
            UNICODE_STRING FileName = {0};

            if (Data->Iopb->Parameters.DirectoryControl.QueryDirectory.FileIndex == SL_INDEX_SPECIFIED)//SL_RESTART_SCAN SL_RETURN_SINGLE_ENTRY
            {
                /*
                注意有的是一下子枚举完毕,有的是一个一个的枚举。
                下同。
                */
            }

            KdPrint(("查询目录的类型:FileBothDirectoryInformation\n"));

            do
            { 
                FileName.Buffer = pfbdi->FileName;
                FileName.Length = (USHORT)pfbdi->FileNameLength;
                FileName.MaximumLength = FileName.Length;

                if (pfbdi->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
                    KdPrint(("目录:%wZ\n", &FileName));
                } else {
                    KdPrint(("文件:%wZ\n", &FileName));
                }

                pfbdi = (PFILE_BOTH_DIR_INFORMATION)((PCHAR)(pfbdi) + pfbdi->NextEntryOffset);
            } while( pfbdi->NextEntryOffset != 0 );            
        }

        KdPrint(("\n"));
        break;
    case FileDirectoryInformation://win 7走这里了。走这里的时机不太清楚,机会很少。      
        {
            PFILE_DIRECTORY_INFORMATION  pfdi = (PFILE_DIRECTORY_INFORMATION )p;
            UNICODE_STRING FileName = {0};

            KdPrint(("查询目录的类型:FileDirectoryInformation\n\n"));

            do
            { 
                FileName.Buffer = pfdi->FileName;
                FileName.Length = (USHORT)pfdi->FileNameLength;
                FileName.MaximumLength = FileName.Length;

                if (pfdi->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
                    KdPrint(("目录:%wZ\n", &FileName));
                } else {
                    KdPrint(("文件:%wZ\n", &FileName));
                }

                pfdi = (PFILE_DIRECTORY_INFORMATION )((PCHAR)(pfdi) + pfdi->NextEntryOffset);
            } while( pfdi->NextEntryOffset != 0 );            
        }

        KdPrint(("\n"));
        break;
    case FileFullDirectoryInformation://server 2008的dir /a 走这里了。
        {
            PFILE_FULL_DIR_INFORMATION  pffdi = (PFILE_FULL_DIR_INFORMATION )p;
            UNICODE_STRING FileName = {0};

            KdPrint(("查询目录的类型:FileFullDirectoryInformation\n"));

            do
            { 
                FileName.Buffer = pffdi->FileName;
                FileName.Length = (USHORT)pffdi->FileNameLength;
                FileName.MaximumLength = FileName.Length;

                if (pffdi->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
                    KdPrint(("目录:%wZ\n", &FileName));
                } else {
                    KdPrint(("文件:%wZ\n", &FileName));
                }

                pffdi = (PFILE_FULL_DIR_INFORMATION )((PCHAR)(pffdi) + pffdi->NextEntryOffset);
            } while( pffdi->NextEntryOffset != 0 );            
        }

        KdPrint(("\n"));
        break;
    case FileIdBothDirectoryInformation://server 2008也走这里了。
        {
            PFILE_ID_BOTH_DIR_INFORMATION  pfibdi = (PFILE_ID_BOTH_DIR_INFORMATION )p;
            UNICODE_STRING FileName = {0};

            KdPrint(("查询目录的类型:FileIdBothDirectoryInformation\n"));

            do
            { 
                FileName.Buffer = pfibdi->FileName;
                FileName.Length = (USHORT)pfibdi->FileNameLength;
                FileName.MaximumLength = FileName.Length;

                if (pfibdi->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
                    KdPrint(("目录:%wZ\n", &FileName));
                } else {
                    KdPrint(("文件:%wZ\n", &FileName));
                }

                pfibdi = (PFILE_ID_BOTH_DIR_INFORMATION )((PCHAR)(pfibdi) + pfibdi->NextEntryOffset);
            } while( pfibdi->NextEntryOffset != 0 );            
        }
        KdPrint(("\n"));
        break;
    case FileIdFullDirectoryInformation://这个及上面的都有FileAttributes属性。
        KdPrint(("查询目录的类型:FileIdFullDirectoryInformation\n\n"));
        break;
    case FileNamesInformation://XP有时也走这里,如启动一个程序。win 7 也走这里了。
        {
            PFILE_NAMES_INFORMATION  pfni2 = (PFILE_NAMES_INFORMATION )p;
            UNICODE_STRING FileName = {0};
            UNICODE_STRING FilePathName = {0};
            FILE_STANDARD_INFORMATION fsi = {0};
            IO_STATUS_BLOCK  IoStatusBlock ={0};
            HANDLE  FileHandle = 0;//(HANDLE)-1;
            OBJECT_ATTRIBUTES ob = {0};
            UNICODE_STRING dot  = RTL_CONSTANT_STRING(L".");
            UNICODE_STRING double_dot  = RTL_CONSTANT_STRING(L"..");

            KdPrint(("查询目录的类型:FileNamesInformation\n"));

            FilePathName.MaximumLength = MAX_PATH;//(USHORT)BufferSizeNeeded + 2;
            FilePathName.Buffer = ExAllocatePoolWithTag( NonPagedPool, FilePathName.MaximumLength, TAG );
            if (FilePathName.Buffer == NULL) {
                break;
            }
            RtlZeroMemory(FilePathName.Buffer, FilePathName.MaximumLength);

            do
            { 
                FileName.Buffer = pfni2->FileName;
                FileName.Length = (USHORT)pfni2->FileNameLength;
                FileName.MaximumLength = FileName.Length;

                RtlCopyUnicodeString(&FilePathName, &pfni->Name); 

                if (FilePathName.Buffer[FilePathName.Length/2 -1] != L'\\')
                {
                    status = RtlAppendUnicodeToString( &FilePathName, L"\\");
                    if (!NT_SUCCESS( status )) {
                        break;
                    } 
                }
                if (RtlCompareUnicodeString(&FileName, &dot, TRUE) == 0 || RtlCompareUnicodeString(&FileName, &double_dot, TRUE) == 0)
                {
                    FilePathName.Length -= 2;
                }
                else
                {
                    status = RtlAppendUnicodeStringToString( &FilePathName, &FileName);
                    if (!NT_SUCCESS( status )) {
                        break;
                    }
                }
                InitializeObjectAttributes(&ob, &FilePathName, 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)) 
                {
                    status = ZwOpenFile(&FileHandle, GENERIC_READ | SYNCHRONIZE, &ob, &IoStatusBlock, FILE_SHARE_READ, FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT);
                    if (!NT_SUCCESS (status))
                    {
                        KdPrint(("\nZwOpenFile fail %wZ with 0x%x.\n", &FilePathName, status));//pagefile.sys和HIVE文件会打不开。
                        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"));
                        }
                        break;
                    }
                }

                //status = FltQueryInformationFile(FltObjects->Instance, FltObjects->FileObject, &fsi, sizeof(FILE_STANDARD_INFORMATION), FileStandardInformation, &LengthReturned);
                status = ZwQueryInformationFile(FileHandle, &IoStatusBlock, &fsi, sizeof(FILE_STANDARD_INFORMATION), FileStandardInformation);
                if (!NT_SUCCESS( status )) 
                {
                    if (FileHandle)
                    {
                        status = ZwClose(FileHandle);
                        if (!NT_SUCCESS (status)) 
                        {
                            KdPrint(("ZwClose fail with 0x%x.\n", status));
                        }
                    }
                    break;
                }

                if (fsi.Directory == TRUE )
                {
                    KdPrint(("目录的名字:%wZ\n", &FilePathName));//注意这个有几个没有显示:点和双点及汉字的等。
                }
                else
                {
                    KdPrint(("文件的名字:%wZ\n", &FilePathName));//FilePathName  FileName
                }     

                if (FileHandle)
                {
                    status = ZwClose(FileHandle);
                    if (!NT_SUCCESS (status)) 
                    {
                        KdPrint(("ZwClose fail with 0x%x.\n", status));
                    }
                }

                pfni2 = (PFILE_NAMES_INFORMATION )((PCHAR)(pfni2) + pfni2->NextEntryOffset);
            } while( pfni2->NextEntryOffset != 0 );

            ExFreePoolWithTag(FilePathName.Buffer, TAG);
        }

        KdPrint(("\n"));
        break;
        //这两个对应的结构没有NextEntryOffset成员,并且上面的结构的第一个成员都是NextEntryOffset。
        //case FileObjectIdInformation:
        //    break;
        //case FileReparsePointInformation:
        //    break;
    default://其实还有更多,不信你看头文件,注意对应的结构的处理。
        KdPrint(("查询目录的类型:FileInformationClass:%d\n", Data->Iopb->Parameters.DirectoryControl.QueryDirectory.FileInformationClass));
        break;
    }   

    FltReleaseFileNameInformation(pfni); 
    return FLT_POSTOP_FINISHED_PROCESSING;
}


CONST FLT_OPERATION_REGISTRATION Callbacks[] = {
    { IRP_MJ_DIRECTORY_CONTROL,  0, 0, DirectoryControlPostOperation},
    { IRP_MJ_OPERATION_END }
};


#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)
{
    FltUnregisterFilter( gFilterHandle );
    return STATUS_SUCCESS;
}


CONST FLT_REGISTRATION FilterRegistration = {
    sizeof( FLT_REGISTRATION ),         //  Size
    FLT_REGISTRATION_VERSION,           //  Version
    0,                                  //  Flags
    NULL,                               //  Context
    Callbacks,                          //  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;

    UNREFERENCED_PARAMETER( RegistryPath );

    KdBreakPoint();

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

    status = FltStartFiltering( gFilterHandle );
    if (!NT_SUCCESS( status )) {
        FltUnregisterFilter( gFilterHandle );
    }

    return status;
}