2016年8月31日星期三

枚举ALE的endpoints

/*
目的:演示FwpsAleEndpointEnum的用法。
注意:编译的平台和运行的平台。
说明:Ale的Endpoint,这是什么呢?
      类似TCPView.exe不停的显示和更新,
      当然这个函数每次获取的值/数量也是不同的。

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

#include <ntifs.h>
#include <windef.h>
#include <ntddk.h>
#include <ntstrsafe.h>
#include <Fwpsk.h>
#include <initguid.h> //静态定义UUID用的,否则:error LNK2001。
#include <Fwpmk.h>
#include <ndis.h>
#include <Wsk.h>
#include <ipmib.h>
#include <netpnp.h>
#include <ntintsafe.h>

#include <intrin.h> //VS2012编译。
#include <immintrin.h>//VS2012编译。
//#include <mmintrin.h> //WDK 编译。
//#include <emmintrin.h>//WDK 编译。
//#include <xmmintrin.h>//WDK 编译。

#pragma warning(disable:4100) //未引用的形参
#pragma warning(disable:4214) //整形以外的位域类型
#pragma warning(disable:4121) //封装要区分成员对齐方式

#define TAG 'test' //test

//https://msdn.microsoft.com/en-us/library/windows/desktop/ms738518%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396
//其实:IPV4的只需This buffer should be large enough to hold at least 16 characters.
#define MAX_ADDRESS_STRING_LENGTH   64


wchar_t * get_protocol_name(UINT8 protocol)
{
    wchar_t * protocol_name = 0;

    switch(protocol)
    {
    case IPPROTO_TCP:
        protocol_name = L"TCP";
        break;
    case IPPROTO_UDP:
        protocol_name = L"UDP";
        break;
    case IPPROTO_IPV4:
        protocol_name = L"IPV4";
        break;
    case IPPROTO_IPV6:
        protocol_name = L"IPV6";
        break;
    case IPPROTO_ICMP:
        protocol_name = L"ICMP";
        break;
    case IPPROTO_IGMP:
        protocol_name = L"IGMP";
        break;
    case IPPROTO_ICMPV6:
        protocol_name = L"ICMPV6";
        break;
    default:
        protocol_name = L"未知";//也可打印一个数值。
        break;
    }

    return protocol_name;
}


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

}


//DRIVER_INITIALIZE DriverEntry;
NTSTATUS DriverEntry(__in struct _DRIVER_OBJECT  * DriverObject, __in PUNICODE_STRING  RegistryPath)
{
    HANDLE gEngineHandle;
    NTSTATUS Status = STATUS_SUCCESS;
    FWPM_SESSION0 session = {0};
    FWPS_ALE_ENDPOINT_ENUM_TEMPLATE0 enumTemplate;//编译为WIN 7版本,VISTA是没有这个变量的。
    HANDLE enumHandle;
    FWPS_ALE_ENDPOINT_PROPERTIES0 **entries;
    UINT32                        numEntriesReturned;
    UINT32 calloutIndex = 0;
    UNICODE_STRING appId = {0};

    //KdBreakPoint();
    __debugbreak();

    DriverObject->DriverUnload = Unload;

    session.flags = FWPM_SESSION_FLAG_DYNAMIC;
    Status = FwpmEngineOpen0(NULL, RPC_C_AUTHN_WINNT, NULL, &session, &gEngineHandle);
    ASSERT (NT_SUCCESS(Status));

    RtlZeroMemory(&enumTemplate, sizeof(FWPS_ALE_ENDPOINT_ENUM_TEMPLATE0));
    Status = FwpsAleEndpointCreateEnumHandle0(gEngineHandle, &enumTemplate, &enumHandle);//Available starting with Windows 7
    ASSERT (NT_SUCCESS(Status));

    Status = FwpsAleEndpointEnum0(gEngineHandle, enumHandle, 0xFFFFFFFF, &entries, &numEntriesReturned);//Available starting with Windows 7
    ASSERT (NT_SUCCESS(Status));

    KdPrint(("numEntriesReturned:0x%x.\n\n\n", numEntriesReturned));

    for( ; calloutIndex < numEntriesReturned; calloutIndex++)
    {
        KdPrint(("calloutIndex:0x%x.\n",(calloutIndex + 1)));

        if (entries[calloutIndex]->ipVersion == FWP_IP_VERSION_V4)
        {
            UINT32 l4a = RtlUlongByteSwap((ULONG)entries[calloutIndex]->localV4Address);
            UINT32 r4a = RtlUlongByteSwap((ULONG)entries[calloutIndex]->remoteV4Address);
            wchar_t localV4Address[MAX_ADDRESS_STRING_LENGTH] = {0};
            wchar_t remoteV4Address[MAX_ADDRESS_STRING_LENGTH] = {0};

            RtlIpv4AddressToString((const struct in_addr *)&l4a, localV4Address);
            RtlIpv4AddressToString((const struct in_addr *)&r4a, remoteV4Address);

            KdPrint(("ipVersion:FWP_IP_VERSION_V4.\n"));
            KdPrint(("localV4Address:%ws.\n", localV4Address));
            KdPrint(("remoteV4Address:%ws.\n", remoteV4Address));
        }
        else if (entries[calloutIndex]->ipVersion == FWP_IP_VERSION_V6)
        {
            /*
            这个IPV6的地址的字节序需要转换不?
            最大是RtlUlonglongByteSwap,64-bit,但是IPv6 is 128 bits。
            难道要用这个函数两次,两次的也不符合,自己写个函数?自己可以验证下。
            */
            wchar_t localV6Address[MAX_ADDRESS_STRING_LENGTH] = {0};
            wchar_t remoteV6Address[MAX_ADDRESS_STRING_LENGTH] = {0};

            RtlIpv6AddressToString((const struct in6_addr *)&entries[calloutIndex]->localV6Address, localV6Address);
            RtlIpv6AddressToString((const struct in6_addr *)&entries[calloutIndex]->remoteV6Address, remoteV6Address);

            KdPrint(("ipVersion:FWP_IP_VERSION_V6.\n"));
            KdPrint(("localV6Address:%ws.\n", localV6Address));
            KdPrint(("remoteV6Address:%ws.\n", remoteV6Address));
        }
        else
        {
            KdBreakPoint();
        }

        KdPrint(("ipProtocol:%ws.\n", get_protocol_name(entries[calloutIndex]->ipProtocol)));
        KdPrint(("localPort:%d.\n", entries[calloutIndex]->localPort));
        KdPrint(("remotePort:%d.\n", entries[calloutIndex]->remotePort));
        KdPrint(("localTokenModifiedId:0x%x.\n", entries[calloutIndex]->localTokenModifiedId));
        KdPrint(("mmSaId:0x%x.\n", entries[calloutIndex]->mmSaId));
        KdPrint(("qmSaId:0x%x.\n", entries[calloutIndex]->qmSaId));
        KdPrint(("ipsecStatus:0x%x.\n", entries[calloutIndex]->ipsecStatus));
        KdPrint(("flags:0x%x.\n", entries[calloutIndex]->flags));

        appId.Buffer = (PWCH)&entries[calloutIndex]->appId.data[0];
        appId.Length = (USHORT)entries[calloutIndex]->appId.size;
        appId.MaximumLength = appId.Length;
        KdPrint(("appId:%wZ.\n", &appId));//进程的完整路径(NT式的设备路径)。一个特殊是:System。

        KdPrint(("\n"));
    }

    FwpmFreeMemory((VOID**)&entries);
    Status = FwpsAleEndpointDestroyEnumHandle0(gEngineHandle, enumHandle);//Available starting with Windows 7
    ASSERT (NT_SUCCESS(Status));

    FwpmEngineClose0(gEngineHandle);

    return Status;
}

2016年8月23日星期二

获取WFP的Bfe的State及相关

/*
起因:系统启动的时候FwpmEngineOpen0返回c0020035即EPT_NT_CANT_PERFORM_OP ,意思是:The operation cannot be performed.(服务器终结点无法运行操作)。
所以:有此文。

文件名:BfeState.C

测试要点和心得:
1.FwpmBfeStateGet0在系统启动的时候返回值是FWPM_SERVICE_STOPPED。
2.FwpmBfeStateGet0在正常驱动加载的时候返回值是FWPM_SERVICE_RUNNING。
3.只有在系统启动的时候会调用FwpmBfeStateSubscribeChanges的回调函数。两次,第一次是FWPM_SERVICE_START_PENDING,后来FWPM_SERVICE_RUNNING。
4.在正常的驱动加载时,不会调用FwpmBfeStateSubscribeChanges的回调函数。
5.所以FwpmBfeStateGet可判断驱动是否在系统启动的时候运行/加载。
...,更多的你想吧,如:启动的时候如何完美的获取EngineHandle。
6.驱动卸载和关机时是没有反应的,可以注册电源事件的监控看看。
7.在系统启动时,从加载驱动到FwpmBfeStateSubscribeChanges的回调函数被调用,这一段时间是很长的:前者很早,后者已经有启动画面了。

WDK说明:
A callout driver calls the FwpmBfeStateSubscribeChanges0 function to register a callback function that is called whenever there is a change to the state of the filter engine.
A callout driver cannot open a session to the filter engine unless the filter engine is currently running.
A callout driver can use the FWPM_SERVICE_RUNNING notification to open a session to the filter engine so that it can make calls to the other Windows Filtering Platform management functions.
Similarly, a callout driver can use the FWPM_SERVICE_STOP_PENDING notification to perform any cleanup before the filter engine is stopped.

A callout driver can retrieve the current state of the filter engine at any time by calling the FwpmBfeStateGet0 function.

A callout driver must deregister the callback function by calling the FwpmBfeStateUnsubscribeChanges0 function before the callout driver can be unloaded.

made by correy
made at 2016.08.23
http://correy.webs.com
*/

#pragma once

#include <ntifs.h>
#include <Fwpsk.h>
#include <windef.h>
#include <initguid.h> //静态定义UUID用的,否则:error LNK2001。
#include <Fwpmk.h>
#include <Ntstrsafe.h>
#include <ndis.h>
#include <Wsk.h>
#include <ipmib.h>
#include <netpnp.h>
#include <ntintsafe.h>

#pragma warning(disable:4047)
#pragma warning(disable:4028)

#define TAG 'test' //test

PDEVICE_OBJECT gDeviceObject;

HANDLE gEngineHandle;
HANDLE gChangeHandle;


VOID Unload(IN PDRIVER_OBJECT DriverObject)
{
    NTSTATUS status;
    UNICODE_STRING uniWin32NameString;
 
    FwpmBfeStateUnsubscribeChanges0(gChangeHandle);

    if (gEngineHandle)
    {
        FwpmEngineClose0(gEngineHandle);
    }

    RtlInitUnicodeString( &uniWin32NameString, L"\\DosDevices\\hpm" );
    IoDeleteSymbolicLink( &uniWin32NameString );

    IoDeleteDevice(gDeviceObject);// Delete the device object
}


VOID NTAPI SubscriptionBFEStateChangeCallback(IN OUT void  *context, IN FWPM_SERVICE_STATE  newState)
//VOID SubscriptionBFEStateChangeCallback(_Inout_ VOID* pContext, _In_ FWPM_SERVICE_STATE bfeState)
/**
   Purpose:  Callback, invoked on BFE service state change, which will get or release a handle to the engine.
   MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF550062.aspx
   测试心得:
   1.操作系统启动的时候先来FWPM_SERVICE_START_PENDING,后来FWPM_SERVICE_RUNNING。
   2.正常的启动是不会走这里的。
*/
{
    NTSTATUS status;
    FWPM_SESSION0 session = {0};

    switch(newState)
    {
    case FWPM_SERVICE_RUNNING:
        //调用FwpmEngineOpen获取EngineHandle。
        KdPrint(("FWPM_SERVICE_RUNNING.\n",__LINE__));

        session.flags = FWPM_SESSION_FLAG_DYNAMIC;
        status = FwpmEngineOpen0(NULL, RPC_C_AUTHN_WINNT, NULL, &session, &gEngineHandle);
        ASSERT(NT_SUCCESS(status));

        break;
    case FWPM_SERVICE_STOP_PENDING://要走这里,还得做一些小动作。
        //调用FwpmEngineClose释放EngineHandle。
        KdPrint(("FWPM_SERVICE_RUNNING.\n",__LINE__));
        break;
    case FWPM_SERVICE_STOPPED:
        KdPrint(("FWPM_SERVICE_STOPPED.\n",__LINE__));//系统启动的时候会是这个。
        break;
    case FWPM_SERVICE_START_PENDING :
        KdPrint(("FWPM_SERVICE_START_PENDING.\n",__LINE__));//操作系统启动的时候会有这个。
        break;
    }
}


NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
{
    NTSTATUS NtStatus = STATUS_SUCCESS;
    UNICODE_STRING  ntUnicodeString;
    UNICODE_STRING  ntWin32NameString;

    KdBreakPoint();

    DriverObject->DriverUnload = Unload;

    RtlInitUnicodeString( &ntUnicodeString, L"\\Device\\hpm");
    NtStatus = IoCreateDevice(DriverObject, 0, &ntUnicodeString, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &gDeviceObject);
    if (!NT_SUCCESS(NtStatus))
    {
        return NtStatus;
    }

    RtlInitUnicodeString( &ntWin32NameString, L"\\DosDevices\\hpm" );
    NtStatus = IoCreateSymbolicLink(&ntWin32NameString, &ntUnicodeString );
    if (!NT_SUCCESS(NtStatus))
    {
        IoDeleteDevice( gDeviceObject );
        return NtStatus;
    }

    SubscriptionBFEStateChangeCallback(NULL, FwpmBfeStateGet0());//这个可判断驱动是否在系统的启动状态。

    NtStatus = FwpmBfeStateSubscribeChanges0(gDeviceObject, SubscriptionBFEStateChangeCallback, NULL, &gChangeHandle);
    if (!NT_SUCCESS(NtStatus))
    {
        IoDeleteSymbolicLink( &ntWin32NameString );
        IoDeleteDevice(gDeviceObject);
        return NtStatus;
    }

    return NtStatus;
}

演示ObCreateObjectType的用法

/*
目的:演示ObCreateObjectType的用法(XP到WIN10)。
注意:编译的平台和运行的平台。

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

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

#include <intrin.h> //VS2012编译。
#include <immintrin.h>//VS2012编译。
//#include <mmintrin.h> //WDK 编译。
//#include <emmintrin.h>//WDK 编译。
//#include <xmmintrin.h>//WDK 编译。

#pragma warning(disable:4100) //未引用的形参
#pragma warning(disable:4214) //整形以外的位域类型
#pragma warning(disable:4121) //封装要区分成员对齐方式

typedef struct _OBJECT_DUMP_CONTROL {
    PVOID Stream;
    ULONG Detail;
} OB_DUMP_CONTROL, *POB_DUMP_CONTROL;

typedef VOID(*OB_DUMP_METHOD)(
    IN PVOID Object,
    IN POB_DUMP_CONTROL Control OPTIONAL
    );

typedef enum _OB_OPEN_REASON {
    ObCreateHandle,
    ObOpenHandle,
    ObDuplicateHandle,
    ObInheritHandle,
    ObMaxOpenReason
} OB_OPEN_REASON;


typedef NTSTATUS(*OB_OPEN_METHOD)(
    IN OB_OPEN_REASON OpenReason,
    IN PEPROCESS Process OPTIONAL,
    IN PVOID Object,
    IN ACCESS_MASK GrantedAccess,
    IN ULONG HandleCount
    );

typedef BOOLEAN(*OB_OKAYTOCLOSE_METHOD)(
    IN PEPROCESS Process OPTIONAL,
    IN PVOID Object,
    IN HANDLE Handle,
    IN KPROCESSOR_MODE PreviousMode
    );

typedef VOID(*OB_CLOSE_METHOD)(
    IN PEPROCESS Process OPTIONAL,
    IN PVOID Object,
    IN ACCESS_MASK GrantedAccess,
    IN ULONG_PTR ProcessHandleCount,
    IN ULONG_PTR SystemHandleCount
    );

typedef VOID(*OB_DELETE_METHOD)(
    IN  PVOID   Object
    );

typedef NTSTATUS(*OB_PARSE_METHOD)(
    IN PVOID ParseObject,
    IN PVOID ObjectType,
    IN OUT PACCESS_STATE AccessState,
    IN KPROCESSOR_MODE AccessMode,
    IN ULONG Attributes,
    IN OUT PUNICODE_STRING CompleteName,
    IN OUT PUNICODE_STRING RemainingName,
    IN OUT PVOID Context OPTIONAL,
    IN PSECURITY_QUALITY_OF_SERVICE SecurityQos OPTIONAL,
    OUT PVOID *Object
    );

typedef NTSTATUS(*OB_SECURITY_METHOD)(
    IN PVOID Object,
    IN SECURITY_OPERATION_CODE OperationCode,
    IN PSECURITY_INFORMATION SecurityInformation,
    IN OUT PSECURITY_DESCRIPTOR SecurityDescriptor,
    IN OUT PULONG CapturedLength,
    IN OUT PSECURITY_DESCRIPTOR *ObjectsSecurityDescriptor,
    IN POOL_TYPE PoolType,
    IN PGENERIC_MAPPING GenericMapping
    );

typedef NTSTATUS(*OB_QUERYNAME_METHOD)(
    IN PVOID Object,
    IN BOOLEAN HasObjectName,
    OUT POBJECT_NAME_INFORMATION ObjectNameInfo,
    IN ULONG Length,
    OUT PULONG ReturnLength,
    IN KPROCESSOR_MODE Mode
    );

#if (NTDDI_VERSION < NTDDI_VISTA)
typedef struct _OBJECT_TYPE_INITIALIZER {
    USHORT Length;
    BOOLEAN UseDefaultObject;
    BOOLEAN CaseInsensitive;
    ULONG InvalidAttributes;
    GENERIC_MAPPING GenericMapping;
    ULONG ValidAccessMask;
    BOOLEAN SecurityRequired;
    BOOLEAN MaintainHandleCount;
    BOOLEAN MaintainTypeList;
    POOL_TYPE PoolType;
    ULONG DefaultPagedPoolCharge;
    ULONG DefaultNonPagedPoolCharge;
    OB_DUMP_METHOD DumpProcedure;
    OB_OPEN_METHOD OpenProcedure;
    OB_CLOSE_METHOD CloseProcedure;
    OB_DELETE_METHOD DeleteProcedure;
    OB_PARSE_METHOD ParseProcedure;
    OB_SECURITY_METHOD SecurityProcedure;
    OB_QUERYNAME_METHOD QueryNameProcedure;
    OB_OKAYTOCLOSE_METHOD OkayToCloseProcedure;
} OBJECT_TYPE_INITIALIZER, *POBJECT_TYPE_INITIALIZER;
#else
__declspec(align(2))
//#pragma pack(2)
#pragma warning(push)
#pragma warning(disable: 4201) //NAMELESS_STRUCT_UNION
typedef struct _OBJECT_TYPE_INITIALIZER {
    USHORT Length;

    union //这里是不是一个联合还有待考察。可怀疑ObjectTypeFlags和下面的一个位结构构成两个字节。
    {
        UCHAR   ObjectTypeFlags;//ObjectTypeFlags就是下面的位结构,总共占用2字节,但是只用一字节。

        struct
        {
            unsigned char  CaseInsensitive : 1;
            unsigned char  UnnamedObjectsOnly : 1;
            unsigned char  UseDefaultObject : 1;
            unsigned char  SecurityRequired : 1;
            unsigned char  MaintainHandleCount : 1;
            unsigned char  MaintainTypeList : 1;
            unsigned char  SupportsObjectCallbacks : 1;
            unsigned char  CacheAligned : 1;
        };
    }ObjectTypeFlags;

    ULONG ObjectTypeCode;
    ULONG InvalidAttributes;
    GENERIC_MAPPING GenericMapping;
    ULONG ValidAccessMask;
    ULONG RetainAccess;
    POOL_TYPE PoolType;
    ULONG DefaultPagedPoolCharge;
    ULONG DefaultNonPagedPoolCharge;
    OB_DUMP_METHOD DumpProcedure;
    OB_OPEN_METHOD OpenProcedure;
    OB_CLOSE_METHOD CloseProcedure;
    OB_DELETE_METHOD DeleteProcedure;
    OB_PARSE_METHOD ParseProcedure;
    OB_SECURITY_METHOD SecurityProcedure;
    OB_QUERYNAME_METHOD QueryNameProcedure;
    OB_OKAYTOCLOSE_METHOD OkayToCloseProcedure;
#if 0 //WIN 10 特有的。因为编译环境是VS2012,所以就不再做特别的处理了。
    ULONG  WaitObjectFlagMask;
    USHORT WaitObjectFlagOffset;
    USHORT WaitObjectPointerOffset;
#endif
} OBJECT_TYPE_INITIALIZER, *POBJECT_TYPE_INITIALIZER;
#pragma warning(pop)
#endif


/*
WIN10 64
1: kd> dt _OBJECT_TYPE_INITIALIZER
ntdll!_OBJECT_TYPE_INITIALIZER
+0x000 Length           : Uint2B
+0x002 ObjectTypeFlags  : UChar
+0x002 CaseInsensitive  : Pos 0, 1 Bit
+0x002 UnnamedObjectsOnly : Pos 1, 1 Bit
+0x002 UseDefaultObject : Pos 2, 1 Bit
+0x002 SecurityRequired : Pos 3, 1 Bit
+0x002 MaintainHandleCount : Pos 4, 1 Bit
+0x002 MaintainTypeList : Pos 5, 1 Bit
+0x002 SupportsObjectCallbacks : Pos 6, 1 Bit
+0x002 CacheAligned     : Pos 7, 1 Bit
+0x004 ObjectTypeCode   : Uint4B
+0x008 InvalidAttributes : Uint4B
+0x00c GenericMapping   : _GENERIC_MAPPING
+0x01c ValidAccessMask  : Uint4B
+0x020 RetainAccess     : Uint4B
+0x024 PoolType         : _POOL_TYPE
+0x028 DefaultPagedPoolCharge : Uint4B
+0x02c DefaultNonPagedPoolCharge : Uint4B
+0x030 DumpProcedure    : Ptr64     void
+0x038 OpenProcedure    : Ptr64     long
+0x040 CloseProcedure   : Ptr64     void
+0x048 DeleteProcedure  : Ptr64     void
+0x050 ParseProcedure   : Ptr64     long
+0x058 SecurityProcedure : Ptr64     long
+0x060 QueryNameProcedure : Ptr64     long
+0x068 OkayToCloseProcedure : Ptr64     unsigned char
+0x070 WaitObjectFlagMask : Uint4B
+0x074 WaitObjectFlagOffset : Uint2B
+0x076 WaitObjectPointerOffset : Uint2B


ObCreateObjectTypeEx中有如下的代码:
if ( !TypeName
|| (Length = TypeName->Length) == 0
|| Length & 1
|| !ObjectTypeInitializer
|| ObjectTypeInitializer->InvalidAttributes & 0xFFFEE00D
|| ObjectTypeInitializer->Length != 120
|| (ObjectTypeFlags = ObjectTypeInitializer->ObjectTypeFlags, ObjectTypeFlags & 0x10) && !ObjectTypeInitializer->OpenProcedure && !ObjectTypeInitializer->CloseProcedure
|| !(ObjectTypeFlags & 4) && ObjectTypeInitializer->PoolType & 0xFFFFFDFF && !((unsigned __int8)ObjectType & 1) )
{
DbgPrintEx(0i64, 0i64, "Error creating object type\n");
__debugbreak();
JUMPOUT(loc_1405CF068);
}
而WRK中是这样的:
PoolType = ObjectTypeInitializer->PoolType;
if ((!TypeName) || (!TypeName->Length) || (TypeName->Length % sizeof( WCHAR )) ||
(ObjectTypeInitializer == NULL) ||
(ObjectTypeInitializer->InvalidAttributes & ~OBJ_ALL_VALID_ATTRIBUTES) ||
(ObjectTypeInitializer->Length != sizeof( *ObjectTypeInitializer )) ||
(ObjectTypeInitializer->MaintainHandleCount && (ObjectTypeInitializer->OpenProcedure == NULL && ObjectTypeInitializer->CloseProcedure == NULL )) ||
((!ObjectTypeInitializer->UseDefaultObject) && (PoolType != NonPagedPool)))
{
return( STATUS_INVALID_PARAMETER );
}


而XP32上是这样的:
0: kd> dt _OBJECT_TYPE_INITIALIZER
ntdll!_OBJECT_TYPE_INITIALIZER
+0x000 Length           : Uint2B
+0x002 UseDefaultObject : UChar
+0x003 CaseInsensitive  : UChar
+0x004 InvalidAttributes : Uint4B
+0x008 GenericMapping   : _GENERIC_MAPPING
+0x018 ValidAccessMask  : Uint4B
+0x01c SecurityRequired : UChar
+0x01d MaintainHandleCount : UChar
+0x01e MaintainTypeList : UChar
+0x020 PoolType         : _POOL_TYPE
+0x024 DefaultPagedPoolCharge : Uint4B
+0x028 DefaultNonPagedPoolCharge : Uint4B
+0x02c DumpProcedure    : Ptr32     void
+0x030 OpenProcedure    : Ptr32     long
+0x034 CloseProcedure   : Ptr32     void
+0x038 DeleteProcedure  : Ptr32     void
+0x03c ParseProcedure   : Ptr32     long
+0x040 SecurityProcedure : Ptr32     long
+0x044 QueryNameProcedure : Ptr32     long
+0x048 OkayToCloseProcedure : Ptr32     unsigned char

自己定义的结构如下:
1: kd> dt test!_OBJECT_TYPE_INITIALIZER  ffffd001`310a68c0
+0x000 Length           : 0x78
+0x002 ObjectTypeFlags  : <unnamed-tag>
+0x004 ObjectTypeCode   : 0
+0x008 InvalidAttributes : 0x100
+0x00c GenericMapping   : _GENERIC_MAPPING
+0x01c ValidAccessMask  : 0xf0001
+0x020 RetainAccess     : 0
+0x024 PoolType         : 0 ( NonPagedPool )
+0x028 DefaultPagedPoolCharge : 0
+0x02c DefaultNonPagedPoolCharge : 0
+0x030 DumpProcedure    : (null)
+0x038 OpenProcedure    : (null)
+0x040 CloseProcedure   : (null)
+0x048 DeleteProcedure  : (null)
+0x050 ParseProcedure   : (null)
+0x058 SecurityProcedure : (null)
+0x060 QueryNameProcedure : (null)
+0x068 OkayToCloseProcedure : (null)
+0x070 WaitObjectFlagMask : 0
+0x074 WaitObjectFlagOffset : 0
+0x076 WaitObjectPointerOffset : 0
不过要定义和系统的结构(显示)一样的结构也是可以,重在内在的一样。

经验证:ObjectTypeFlags就是下面的位结构,总共占用2字节,但是只用一字节。
而且偏移的第二字节和第三字节的内容是不一样的,大多偏移的第三自己的内容是0.
kd> dt _OBJECT_TYPE_INITIALIZER fffff800`ff4b5220 -b
nt!_OBJECT_TYPE_INITIALIZER
+0x000 Length           : 0x78
+0x002 ObjectTypeFlags  : 0x24 '$'
+0x002 CaseInsensitive  : 0y0
+0x002 UnnamedObjectsOnly : 0y0
+0x002 UseDefaultObject : 0y1
+0x002 SecurityRequired : 0y0
+0x002 MaintainHandleCount : 0y0
+0x002 MaintainTypeList : 0y1
+0x002 SupportsObjectCallbacks : 0y0
+0x002 CacheAligned     : 0y0
+0x004 ObjectTypeCode   : 0
+0x008 InvalidAttributes : 0x100
+0x00c GenericMapping   : _GENERIC_MAPPING
+0x000 GenericRead      : 0x20000
+0x004 GenericWrite     : 0x20000
+0x008 GenericExecute   : 0x20000
+0x00c GenericAll       : 0xf0001
+0x01c ValidAccessMask  : 0xf0001
+0x020 RetainAccess     : 0
+0x024 PoolType         : 200 ( NonPagedPoolNx )
+0x028 DefaultPagedPoolCharge : 0
+0x02c DefaultNonPagedPoolCharge : 0xd8
+0x030 DumpProcedure    : (null)
+0x038 OpenProcedure    : (null)
+0x040 CloseProcedure   : (null)
+0x048 DeleteProcedure  : (null)
+0x050 ParseProcedure   : (null)
+0x058 SecurityProcedure : (null)
+0x060 QueryNameProcedure : (null)
+0x068 OkayToCloseProcedure : (null)
+0x070 WaitObjectFlagMask : 0
+0x074 WaitObjectFlagOffset : 0
+0x076 WaitObjectPointerOffset : 0
kd> .formats 24
Evaluate expression:
Hex:     00000000`00000024
Decimal: 36
Octal:   0000000000000000000044
Binary:  00000000 00000000 00000000 00000000 00000000 00000000 00000000 00100100
Chars:   .......$
Time:    Thu Jan 01 08:00:36 1970
Float:   low 5.04467e-044 high 0
Double:  1.77864e-322

2008 X64的测试效果:
0: kd> dt nt!_OBJECT_TYPE_INITIALIZER -b
   +0x000 Length           : Uint2B
   +0x002 ObjectTypeFlags  : UChar
   +0x002 CaseInsensitive  : Pos 0, 1 Bit
   +0x002 UnnamedObjectsOnly : Pos 1, 1 Bit
   +0x002 UseDefaultObject : Pos 2, 1 Bit
   +0x002 SecurityRequired : Pos 3, 1 Bit
   +0x002 MaintainHandleCount : Pos 4, 1 Bit
   +0x002 MaintainTypeList : Pos 5, 1 Bit
   +0x002 SupportsObjectCallbacks : Pos 6, 1 Bit
   +0x002 CacheAligned     : Pos 7, 1 Bit
   +0x004 ObjectTypeCode   : Uint4B
   +0x008 InvalidAttributes : Uint4B
   +0x00c GenericMapping   : _GENERIC_MAPPING
      +0x000 GenericRead      : Uint4B
      +0x004 GenericWrite     : Uint4B
      +0x008 GenericExecute   : Uint4B
      +0x00c GenericAll       : Uint4B
   +0x01c ValidAccessMask  : Uint4B
   +0x020 RetainAccess     : Uint4B
   +0x024 PoolType         :
      NonPagedPool = 0n0
      PagedPool = 0n1
      NonPagedPoolMustSucceed = 0n2
      DontUseThisType = 0n3
      NonPagedPoolCacheAligned = 0n4
      PagedPoolCacheAligned = 0n5
      NonPagedPoolCacheAlignedMustS = 0n6
      MaxPoolType = 0n7
      NonPagedPoolSession = 0n32
      PagedPoolSession = 0n33
      NonPagedPoolMustSucceedSession = 0n34
      DontUseThisTypeSession = 0n35
      NonPagedPoolCacheAlignedSession = 0n36
      PagedPoolCacheAlignedSession = 0n37
      NonPagedPoolCacheAlignedMustSSession = 0n38
   +0x028 DefaultPagedPoolCharge : Uint4B
   +0x02c DefaultNonPagedPoolCharge : Uint4B
   +0x030 DumpProcedure    : Ptr64
   +0x038 OpenProcedure    : Ptr64
   +0x040 CloseProcedure   : Ptr64
   +0x048 DeleteProcedure  : Ptr64
   +0x050 ParseProcedure   : Ptr64
   +0x058 SecurityProcedure : Ptr64
   +0x060 QueryNameProcedure : Ptr64
   +0x068 OkayToCloseProcedure : Ptr64
0: kd> dt test!_OBJECT_TYPE_INITIALIZER -b
   +0x000 Length           : Uint2B
   +0x002 ObjectTypeFlags  : <unnamed-tag>
      +0x000 ObjectTypeFlags  : UChar
      +0x000 CaseInsensitive  : Pos 0, 1 Bit
      +0x000 UnnamedObjectsOnly : Pos 1, 1 Bit
      +0x000 UseDefaultObject : Pos 2, 1 Bit
      +0x000 SecurityRequired : Pos 3, 1 Bit
      +0x000 MaintainHandleCount : Pos 4, 1 Bit
      +0x000 MaintainTypeList : Pos 5, 1 Bit
      +0x000 SupportsObjectCallbacks : Pos 6, 1 Bit
      +0x000 CacheAligned     : Pos 7, 1 Bit
   +0x004 ObjectTypeCode   : Uint4B
   +0x008 InvalidAttributes : Uint4B
   +0x00c GenericMapping   : _GENERIC_MAPPING
      +0x000 GenericRead      : Uint4B
      +0x004 GenericWrite     : Uint4B
      +0x008 GenericExecute   : Uint4B
      +0x00c GenericAll       : Uint4B
   +0x01c ValidAccessMask  : Uint4B
   +0x020 RetainAccess     : Uint4B
   +0x024 PoolType         :
      NonPagedPool = 0n0
      NonPagedPoolExecute = 0n0
      PagedPool = 0n1
      NonPagedPoolMustSucceed = 0n2
      DontUseThisType = 0n3
      NonPagedPoolCacheAligned = 0n4
      PagedPoolCacheAligned = 0n5
      NonPagedPoolCacheAlignedMustS = 0n6
      MaxPoolType = 0n7
      NonPagedPoolBase = 0n0
      NonPagedPoolBaseMustSucceed = 0n2
      NonPagedPoolBaseCacheAligned = 0n4
      NonPagedPoolBaseCacheAlignedMustS = 0n6
      NonPagedPoolSession = 0n32
      PagedPoolSession = 0n33
      NonPagedPoolMustSucceedSession = 0n34
      DontUseThisTypeSession = 0n35
      NonPagedPoolCacheAlignedSession = 0n36
      PagedPoolCacheAlignedSession = 0n37
      NonPagedPoolCacheAlignedMustSSession = 0n38
      NonPagedPoolNx = 0n512
      NonPagedPoolNxCacheAligned = 0n516
      NonPagedPoolSessionNx = 0n544
   +0x028 DefaultPagedPoolCharge : Uint4B
   +0x02c DefaultNonPagedPoolCharge : Uint4B
   +0x030 DumpProcedure    : Ptr64
   +0x038 OpenProcedure    : Ptr64
   +0x040 CloseProcedure   : Ptr64
   +0x048 DeleteProcedure  : Ptr64
   +0x050 ParseProcedure   : Ptr64
   +0x058 SecurityProcedure : Ptr64
   +0x060 QueryNameProcedure : Ptr64
   +0x068 OkayToCloseProcedure : Ptr64
*/


NTSTATUS ObCreateObjectType(__in PUNICODE_STRING TypeName, __in POBJECT_TYPE_INITIALIZER ObjectTypeInitializer, __in_opt PSECURITY_DESCRIPTOR SecurityDescriptor, __out POBJECT_TYPE *ObjectType);


POBJECT_TYPE MyObjectType;


//DRIVER_UNLOAD Unload;
VOID Unload(__in PDRIVER_OBJECT DriverObject)
{
    //MyObjectType创建了,如何删除呢?
    ObDereferenceObject(MyObjectType);//不行,依然存在。
}


//DRIVER_INITIALIZE DriverEntry;
NTSTATUS DriverEntry(__in struct _DRIVER_OBJECT  * DriverObject, __in PUNICODE_STRING  RegistryPath)
{
    OBJECT_TYPE_INITIALIZER ObjectTypeInitializer;
    NTSTATUS Status = STATUS_SUCCESS;
    UNICODE_STRING TypeName;
    const GENERIC_MAPPING ExpEventMapping = {
        STANDARD_RIGHTS_READ,
        STANDARD_RIGHTS_WRITE,
        STANDARD_RIGHTS_EXECUTE | SYNCHRONIZE,
        GENERIC_ALL
    };

    //KdBreakPoint();
    __debugbreak();

    DriverObject->DriverUnload = Unload;

    RtlInitUnicodeString(&TypeName, L"Correy");// Initialize string descriptor.
    RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer));
    ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer);
    ObjectTypeInitializer.InvalidAttributes = OBJ_OPENLINK;
    ObjectTypeInitializer.GenericMapping = ExpEventMapping;
    ObjectTypeInitializer.PoolType = NonPagedPool;
    ObjectTypeInitializer.ValidAccessMask = OBJECT_TYPE_ALL_ACCESS;
    //这里还可以定义八个方法/函数。
    Status = ObCreateObjectType(&TypeName, &ObjectTypeInitializer, (PSECURITY_DESCRIPTOR) NULL, &MyObjectType);//XP成功,Win10 64返回参数错误。

#if (NTDDI_VERSION > NTDDI_VISTA)
    //XXX
#endif

    return Status;//在XP上的另一个验证办法:!object \ObjectTypes\Correy
}

2016年8月17日星期三

Windows Filtering Platform 的例子

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



/*
因WFP的资料极度匮乏,特公示。
注释:这是初级的,入门的,更深的等待你挖掘。

这里有两个例子:
1.一个是纯内核驱动的。
2.一个是R0和R3紧密配合使用的。

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



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



//驱动代码如下:

#pragma once

#include <ntifs.h>
#include <Fwpsk.h>
#include <windef.h>
#include <initguid.h> //静态定义UUID用的,否则:error LNK2001。
#include <Fwpmk.h>
#include <Ntstrsafe.h>
#include <ndis.h>
#include <Wsk.h>
#include <ipmib.h>
#include <netpnp.h>
#include <ntintsafe.h>
#include <intrin.h>

#pragma warning(disable:4047)
#pragma warning(disable:4028)

#define TAG 'test' //test

PDEVICE_OBJECT deviceObject;

DEFINE_GUID(CALLOUTKEY, 0x99999999, 0x9999, 0x9999, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99);


VOID NTAPI ClassifyFn(
    IN const FWPS_INCOMING_VALUES0  *inFixedValues,
    IN const FWPS_INCOMING_METADATA_VALUES0  *inMetaValues,
    IN OUT VOID  *layerData,
    IN const FWPS_FILTER0  *filter,
    IN UINT64  flowContext,
    OUT FWPS_CLASSIFY_OUT0  *classifyOut
    )
{
    KdPrint(("ClassifyFn.\r\n"));
}


NTSTATUS NTAPI NotifyFn(IN FWPS_CALLOUT_NOTIFY_TYPE  notifyType, IN const GUID  *filterKey, IN const FWPS_FILTER0  *filter)
{
    return STATUS_SUCCESS;
}


VOID Unload(IN PDRIVER_OBJECT DriverObject)
{
    NTSTATUS status;
    UNICODE_STRING uniWin32NameString;
   
    status = FwpsCalloutUnregisterByKey(&CALLOUTKEY); ASSERT(NT_SUCCESS(status));

    //RtlInitUnicodeString( &uniWin32NameString, L"\\DosDevices\\hpm" );
    //IoDeleteSymbolicLink( &uniWin32NameString );
    IoDeleteDevice(deviceObject);// Delete the device object
}


NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
{
    NTSTATUS NtStatus = STATUS_SUCCESS;
    UNICODE_STRING  ntUnicodeString;
    UNICODE_STRING  ntWin32NameString;
    FWPS_CALLOUT0 sCallout ={0};
    UINT32 CalloutId;// Variable for the run-time callout identifier

    KdBreakPoint();
    __debugbreak();

    DriverObject->DriverUnload = Unload;

    RtlInitUnicodeString( &ntUnicodeString, L"\\Device\\hpm");
    NtStatus = IoCreateDevice(DriverObject, 0, &ntUnicodeString, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &deviceObject);
    ASSERT (NT_SUCCESS( NtStatus ));

    //RtlInitUnicodeString( &ntWin32NameString, L"\\DosDevices\\hpm" );
    //NtStatus = IoCreateSymbolicLink(&ntWin32NameString, &ntUnicodeString );
    //ASSERT (NT_SUCCESS( NtStatus ));

    sCallout.calloutKey = CALLOUTKEY;
    sCallout.classifyFn = ClassifyFn;
    sCallout.notifyFn = NotifyFn;
    NtStatus = FwpsCalloutRegister0(deviceObject, &sCallout, &CalloutId);
    ASSERT (NT_SUCCESS( NtStatus ));

    return NtStatus;
}



//////////////////////////////////////////////////////////////////////////////////////////////////
//应用层的代码如下:



#include <windows.h>
#include <fwpmu.h>
#include <stdio.h>
#include <assert.h>
#include <intrin.h>
#include <conio.h>
#include <initguid.h> //静态定义UUID用的,否则:error LNK2001。

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

const GUID PROVIDER_KEY = {0x5fb216a8, 0xe2e8, 0x4024, { 0xb8, 0x53, 0x39, 0x1a, 0x41, 0x68, 0x64, 0x1e }};
DEFINE_GUID(CALLOUTKEY, 0x99999999, 0x9999, 0x9999, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99);


DWORD Install()
    /*
    功能:开启内核的WFP的Callout。

    参考:
    1.https://msdn.microsoft.com/en-us/library/windows/desktop/bb427376(v=vs.85).aspx
    2.Windows-driver-samples-master\network\trans\msnmntr
    */
{
   HANDLE engine = NULL;
   FWPM_SESSION0 session;
   memset(&session, 0, sizeof(session));  
   session.displayData.name = L"SDK Examples";// The session name isn't required but may be useful for diagnostics.  
   session.txnWaitTimeoutInMSec = INFINITE;// Set an infinite wait timeout, so we don't have to handle FWP_E_TIMEOUT errors while waiting to acquire the transaction lock.  
   session.flags = FWPM_SESSION_FLAG_DYNAMIC;// Let the Base Filtering Engine cleanup after us.
   DWORD result = FwpmEngineOpen0(NULL, RPC_C_AUTHN_DEFAULT, NULL, &session, &engine);// The authentication service should always be RPC_C_AUTHN_DEFAULT.
   assert(ERROR_SUCCESS == result);
 
   result = FwpmTransactionBegin0(engine, 0);// We add the provider and sublayer from within a single transaction to make it easy to clean up partial results in error paths.
   assert(ERROR_SUCCESS == result);

   /*
   此处不是必要的调用:
   1.FwpmProviderAdd0
   2.FwpmSubLayerAdd0
   3.如果加上更好。
   */

   //不添加过滤条件,WFP的内核Callout汗函数也不会调用。
   FWPM_FILTER filter;
   RtlZeroMemory(&filter, sizeof(FWPM_FILTER));
   filter.layerKey = FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4;
   filter.displayData.name = L"name";
   filter.displayData.description = L"description";
   filter.action.type = FWP_ACTION_CALLOUT_INSPECTION; // We're only doing inspection.
   filter.action.calloutKey = CALLOUTKEY;
   filter.weight.type = FWP_EMPTY; // auto-weight.    
   filter.numFilterConditions = 0;
   result = FwpmFilterAdd(engine, &filter, NULL, NULL);
   assert(ERROR_SUCCESS == result);

   // Once all the adds have succeeded, we commit the transaction to persist the new objects.
   result = FwpmTransactionCommit0(engine);
   assert(ERROR_SUCCESS == result);

   _getch();//一调用FwpmEngineClose0效果就没有了,就是内核的ClassifyFn就不运行了。

   // FwpmEngineClose0 accepts null engine handles, so we needn't precheck for null.
   // Also, when closing an engine handle, any transactions still in progress are automatically aborted, so we needn't explicitly abort the transaction in error paths.
   FwpmEngineClose0(engine);
   return result;
}


int _tmain(int argc, _TCHAR* argv[])
{
    DebugBreak();
    __debugbreak();

    //运行/测试前确保驱动加载。
    DWORD D = Install();

    return 0;
}



//////////////////////////////////////////////////////////////////////////////////////////////////
//如果需要SOURCES文件,SOURCES文件内容如下:



TARGETNAME=wfp
TARGETTYPE=DRIVER

LINKER_FLAGS = $(LINKER_FLAGS)/INTEGRITYCHECK

INCLUDES=\
   $(DDK_INC_PATH);

TARGETLIBS=\
    $(DDK_LIB_PATH)\ntoskrnl.lib \
    $(DDK_LIB_PATH)\ndis.lib \
    $(DDK_LIB_PATH)\fwpkclnt.lib \
    $(SDK_LIB_PATH)\uuid.lib
   
TARGETLIBS=$(TARGETLIBS) $(DDK_LIB_PATH)\Ntstrsafe.lib

C_DEFINES=$(C_DEFINES) -DBINARY_COMPATIBLE=0 -DNT -DUNICODE -D_UNICODE -DNDIS60 -DNDIS_SUPPORT_NDIS6

SOURCES= wfp.c



//////////////////////////////////////////////////////////////////////////////////////////////////
/*
纯驱动版的。

注释:可以没有用FwpmSubLayerAdd添加自己的SubLayer,而是用FWPM_SUBLAYER_UNIVERSAL设置subLayerKey
*/


#pragma once

#include <ntifs.h>
#include <Fwpsk.h>
#include <windef.h>
#include <initguid.h> //静态定义UUID用的,否则:error LNK2001。
#include <Fwpmk.h>


// Context structure to be associated with the filters
typedef struct FILTER_CONTEXT_ {
    //.
    //.  // Driver-specific content
    //.
    int test;
} FILTER_CONTEXT, *PFILTER_CONTEXT;

#define FILTER_CONTEXT_POOL_TAG 'fcpt'// Memory pool tag for filter context structures


// Context structure to be associated with data flows
typedef struct FLOW_CONTEXT_ {
    //...
    int test;
} FLOW_CONTEXT, *PFLOW_CONTEXT;

#define FLOW_CONTEXT_POOL_TAG 'fcpt'

PDEVICE_OBJECT deviceObject;
UINT32 CalloutId;// Variable for the run-time callout identifier
FWPS_CALLOUT0 Callout ={0};
HANDLE injectionHandle;// Injection handle
HANDLE gEngineHandle;

DEFINE_GUID(WFP_TEST_GUID, 0x99999999, 0x9999, 0x9999, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99);
DEFINE_GUID(WFP_TEST_LAYER, 0x88888888, 0x8888, 0x8888, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88);


// Prototypes for the callout's callout functions
VOID NTAPI ClassifyFn(
    IN const FWPS_INCOMING_VALUES0  *inFixedValues,
    IN const FWPS_INCOMING_METADATA_VALUES0  *inMetaValues,
    IN OUT VOID  *layerData,
    IN const FWPS_FILTER0  *filter,
    IN UINT64  flowContext,
    OUT FWPS_CLASSIFY_OUT0  *classifyOut
    )
{

}


NTSTATUS NTAPI NotifyFn(
    IN FWPS_CALLOUT_NOTIFY_TYPE  notifyType,
    IN const GUID  *filterKey,
    IN const FWPS_FILTER0  *filter
    )
{
    NTSTATUS NtStatus = STATUS_SUCCESS;
    PFILTER_CONTEXT context;

    ASSERT(filter != NULL);
   
    switch(notifyType)// Switch on the type of notification
    {        
    case FWPS_CALLOUT_NOTIFY_ADD_FILTER:// A filter is being added to the filter engine      
        //context = (PFILTER_CONTEXT)ExAllocatePoolWithTag(NonPagedPool, sizeof(FILTER_CONTEXT), FILTER_CONTEXT_POOL_TAG);// Allocate the filter context structure      
        //if (context == NULL) {// Check the result of the memory allocation          
        //    return STATUS_INSUFFICIENT_RESOURCES;// Return error
        //}

        // Initialize the filter context structure
        //...
       
        //filter->context = (UINT64)context;// Associate the filter context structure with the filter
        break;      
    case FWPS_CALLOUT_NOTIFY_DELETE_FILTER:// A filter is being removed from the filter engine      
        context = (PFILTER_CONTEXT)filter->context;// Get the filter context structure from the filter      
        if (context) {// Check whether the filter has a context

            // Cleanup the filter context structure
            //...

            //ExFreePoolWithTag(context, FILTER_CONTEXT_POOL_TAG);// Free the memory for the filter context structure
        }
        break;  
    case FWPS_CALLOUT_NOTIFY_ADD_FILTER_POST_COMMIT:

        break;
    default:// Unknown notification
        // Do nothing
        break;
    }

    return STATUS_SUCCESS;
    return NtStatus;
}


VOID NTAPI FlowDeleteFn(
    IN UINT16  layerId,
    IN UINT32  calloutId,
    IN UINT64  flowContext
    )
{
    PFLOW_CONTEXT context;

    context = (PFLOW_CONTEXT)flowContext;// Get the flow context structure

    // Cleanup the flow context structure
    //...

    //ExFreePoolWithTag(context, FLOW_CONTEXT_POOL_TAG);// Free the memory for the flow context structure
}


VOID Unload(IN PDRIVER_OBJECT DriverObject)
{
    NTSTATUS status;
 
    FwpmEngineClose0(gEngineHandle);

    status = FwpsCalloutUnregisterById0(CalloutId);// Unregister the callout
    if (status == STATUS_DEVICE_BUSY)// Check result
    {
        // For each data flow that is being processed by the callout that has an associated context,
        // clean up the context and then call FwpsFlowRemoveContext0 to remove the context from the data flow.
        //...

        status = FwpsCalloutUnregisterById0(CalloutId);// Finish unregistering the callout
    }

    if (status != STATUS_SUCCESS)// Check status
    {
        // Handle error
        //...
    }

    IoDeleteDevice(deviceObject);// Delete the device object

    status = FwpsInjectionHandleDestroy0(injectionHandle);// Destroy the injection handle
    if (status != STATUS_SUCCESS)// Check status
    {
        // Handle error
        //...
    }
}


NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
{
    NTSTATUS NtStatus = STATUS_SUCCESS;
    FWPM_SUBLAYER0 SubLayer;
    FWPM_SESSION0 session = {0};
    FWPM_FILTER0 filter = {0};
    FWPM_FILTER_CONDITION0 filterConditions[3] = {0};
    FWPM_CALLOUT0 mCallout = {0};
    FWPM_DISPLAY_DATA0 displayData = {0};

    KdBreakPoint();

    DriverObject->DriverUnload = Unload;// Specify the callout driver's Unload function

    NtStatus = IoCreateDevice(DriverObject, 0, NULL, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &deviceObject);// Create a device object
    if (!NT_SUCCESS( NtStatus )) {
        return NtStatus;
    }

    //WDK文档的Registering Callouts with the Filter Engine章节没有这个函数,可是卸载里有用这个变量。DDPROXY有。
    NtStatus = FwpsInjectionHandleCreate0(AF_INET, FWPS_INJECTION_TYPE_TRANSPORT, &injectionHandle);
    if (!NT_SUCCESS(NtStatus)) {
        IoDeleteDevice(deviceObject);
        return NtStatus;
    }

    session.flags = FWPM_SESSION_FLAG_DYNAMIC;
    NtStatus = FwpmEngineOpen0(NULL, RPC_C_AUTHN_WINNT, NULL, &session, &gEngineHandle);
    if (!NT_SUCCESS(NtStatus)) {      
        FwpsInjectionHandleDestroy0(injectionHandle);
        IoDeleteDevice(deviceObject);
        return NtStatus;
    }

    NtStatus = FwpmTransactionBegin0(gEngineHandle, 0);
    if (!NT_SUCCESS(NtStatus)) {      
        FwpmEngineClose0(gEngineHandle);
        FwpsInjectionHandleDestroy0(injectionHandle);
        IoDeleteDevice(deviceObject);
        return NtStatus;
    }

   RtlZeroMemory(&SubLayer, sizeof(FWPM_SUBLAYER0));
   SubLayer.subLayerKey = WFP_TEST_LAYER;
   SubLayer.displayData.name = L"WFP TEST NAME";
   SubLayer.displayData.description = L"WFP TEST DESCRIPTION";
   SubLayer.flags = 0;
   SubLayer.weight = FWP_EMPTY; // auto-weight.;
   NtStatus = FwpmSubLayerAdd0(gEngineHandle, &SubLayer, NULL);
   if (!NT_SUCCESS(NtStatus)) {
       FwpmEngineClose0(gEngineHandle);
       FwpsInjectionHandleDestroy0(injectionHandle);
       IoDeleteDevice(deviceObject);
       return NtStatus;
   }

    Callout.calloutKey = WFP_TEST_GUID;
    Callout.flags = 0;
    Callout.classifyFn = ClassifyFn;
    Callout.notifyFn = NotifyFn;
    Callout.flowDeleteFn = FlowDeleteFn;
    NtStatus = FwpsCalloutRegister0(deviceObject, &Callout, &CalloutId);
    if (!NT_SUCCESS( NtStatus )) {
        FwpmEngineClose0(gEngineHandle);
        FwpsInjectionHandleDestroy0(injectionHandle);
        IoDeleteDevice(deviceObject);
        return NtStatus;
    }

    displayData.name = L"XXX name";
    displayData.description = L"XXX description";
    mCallout.calloutKey = WFP_TEST_GUID;
    mCallout.displayData = displayData;
    mCallout.applicableLayer = FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4;
    NtStatus = FwpmCalloutAdd0(gEngineHandle, &mCallout, NULL, NULL);
    if (!NT_SUCCESS(NtStatus)) {
        FwpsCalloutUnregisterById0(CalloutId);
        FwpmEngineClose0(gEngineHandle);
        FwpsInjectionHandleDestroy0(injectionHandle);
        IoDeleteDevice(deviceObject);
        return NtStatus;
    }

    filterConditions[0].fieldKey = FWPM_CONDITION_DIRECTION;
    filterConditions[0].matchType = FWP_MATCH_EQUAL;
    filterConditions[0].conditionValue.type = FWP_UINT32;
    filterConditions[0].conditionValue.uint32 = FWP_DIRECTION_OUTBOUND;

    filter.layerKey = FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4;
    filter.displayData.name = L"WFP TEST NAME";
    filter.displayData.description = L"WFP TEST DESCRIPTION";
    filter.action.type = FWP_ACTION_CALLOUT_TERMINATING;
    filter.action.calloutKey = WFP_TEST_GUID;
    filter.filterCondition = filterConditions;
    filter.subLayerKey = WFP_TEST_LAYER;
    filter.weight.type = FWP_EMPTY; // auto-weight.
    filter.rawContext = 0;
    filter.numFilterConditions = 1;
    NtStatus = FwpmFilterAdd0(gEngineHandle, &filter, NULL, NULL);
    if (!NT_SUCCESS(NtStatus)) {
        FwpsCalloutUnregisterById0(CalloutId);
        FwpmEngineClose0(gEngineHandle);
        FwpsInjectionHandleDestroy0(injectionHandle);
        IoDeleteDevice(deviceObject);
        return NtStatus;
    }

    NtStatus = FwpmTransactionCommit0(gEngineHandle);
    if (!NT_SUCCESS(NtStatus)) {
        FwpsCalloutUnregisterById0(CalloutId);
        FwpmEngineClose0(gEngineHandle);
        FwpsInjectionHandleDestroy0(injectionHandle);
        IoDeleteDevice(deviceObject);
        return NtStatus;
    }

    return NtStatus;
}


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

枚举INTEL CPU的CACHE信息

#include "stdafx.h"

/*
功能:枚举INTEL CPU 的各个层次的CACHE信息,如大小,类型等。
英文说法是:enumerate the deterministic cache parameters for each level of the cache hierarchy.

参考:WRK和INTEL等的资料。

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

#include <Windows.h>
#include <assert.h>

#include <stdio.h>
#include <nmmintrin.h>
#include <immintrin.h>
#include <intrin.h>

//一下结构摘自:WRK,注释来自INTEL。

// Structure of Intel deterministic cache information returned by CPUID instruction
typedef enum _INTEL_CACHE_TYPE {//Bits 04-00: Cache Type Field
    IntelCacheNull,       //0 = Null - No more caches
    IntelCacheData,       //1 = Data Cache
    IntelCacheInstruction,//2 = Instruction Cache
    IntelCacheUnified,    //3 = Unified Cache
    IntelCacheRam,        //4-31 = Reserved
    IntelCacheTrace
} INTEL_CACHE_TYPE;

typedef union INTEL_CACHE_INFO_EAX {
    ULONG Ulong;
    struct {
        INTEL_CACHE_TYPE Type : 5; //Bits 04-00: Cache Type Field
        ULONG Level : 3;           //Bits 07-05: Cache Level (starts at 1)
        ULONG SelfInitializing : 1;//Bit 08: Self Initializing cache level (does not need SW initialization)
        ULONG FullyAssociative : 1;//Bit 09: Fully Associative cache
        ULONG Reserved : 4;        //Bits 13-10: Reserved
        ULONG ThreadsSharing : 12; //Bits 25-14: Maximum number of addressable IDs for logical processors sharing this cache**, ***
        ULONG ProcessorCores : 6;  //Bits 31-26: Maximum number of addressable IDs for processor cores in the physical package**, ****, *****
    };
} INTEL_CACHE_INFO_EAX, *PINTEL_CACHE_INFO_EAX;

typedef union INTEL_CACHE_INFO_EBX {
    ULONG Ulong;
    struct {
        ULONG LineSize : 12;     //Bits 11-00: L = System Coherency Line Size**
        ULONG Partitions : 10;   //Bits 21-12: P = Physical Line partitions**
        ULONG Associativity : 10;//Bits 31-22: W = Ways of associativity**
    };
} INTEL_CACHE_INFO_EBX, *PINTEL_CACHE_INFO_EBX;


BOOL is_support_intel()
{
    BOOL B = FALSE;
    char CPUString[0x20];
    int CPUInfo[4] = { -1 };
    unsigned    nIds;

    // __cpuid with an InfoType argument of 0 returns the number of valid Ids in CPUInfo[0] and the CPU identification string in the other three array elements.
    // The CPU identification string is not in linear order.
    // The code below arranges the information in a human readable form.
    __cpuid(CPUInfo, 0);
    nIds = CPUInfo[0];
    memset(CPUString, 0, sizeof(CPUString));
    *((int*) CPUString) = CPUInfo[1];
    *((int*) (CPUString + 4)) = CPUInfo[3];
    *((int*) (CPUString + 8)) = CPUInfo[2];

    //printf_s("\n\nCPU String: %s\n", CPUString);//GenuineIntel
    if (_stricmp(CPUString, "GenuineIntel") == 0)
    {
        B = TRUE;
    }

    return B;
}


BOOL is_support_cpuid()
/*
判断CPU是否支持CPUID指令。
*/
{
    BOOL B = FALSE;
    SIZE_T eflags1;
    SIZE_T eflags2;

    eflags1 = __readeflags();
    __writeeflags(eflags1 | 0x200000);
    eflags2 = __readeflags();
    __writeeflags(eflags1);
    if (eflags1 == eflags2) {
        B = FALSE;
    }
    else {
        B = TRUE;
    }

    return B;
}


int _tmain(int argc, _TCHAR* argv [])
{
    if (!is_support_cpuid())
    {
        return 0;
    }

    if (!is_support_intel())
    {
        return 0;
    }

    int CPUInfo[4] = { -1 };
    unsigned    nExIds;
    __cpuid(CPUInfo, 0);
    nExIds = CPUInfo[0];
    if (nExIds < 4)
    {
        return 0;
    }

    //下面的算法参照WindowsResearchKernel-WRK\WRK-v1.2\base\ntos\ke\amd64\initkr.c文件中的KiSetCacheInformationIntel函数,当然还有INTEL的文档。
    INTEL_CACHE_INFO_EAX CacheInfoEax;
    INTEL_CACHE_INFO_EBX CacheInfoEbx;
    ULONG Index = 0;//Valid index values start from 0.
    ULONGLONG CacheSize;

    int nCores = 0;
    int nCacheType = 0;
    int nCacheLevel = 0;
    int nMaxThread = 0;
    int nSysLineSize = 0;
    int nPhysicalLinePartitions = 0;
    int nWaysAssociativity = 0;
    int nNumberSets = 0;
    int    bSelfInit = false;
    int    bFullyAssociative = false;

    for (;; Index += 1)
    {
        __cpuidex(CPUInfo, 4, Index); //注意:80000006H还有个信息。
        CacheInfoEax.Ulong = CPUInfo[0];
        CacheInfoEbx.Ulong = CPUInfo[1];

        if (CacheInfoEax.Type == IntelCacheNull) {
            break;//下面INTEL也说出了结束的标志。
        }

        //另一种退出方式是:https://msdn.microsoft.com/en-us/library/hskdteyh(v=vs.100).aspx
        if (!(CPUInfo[0] & 0xf0))
            break;

        if (Index == 0)
        {
            nCores = CPUInfo[0] >> 26;
            printf_s("\n\nNumber of Cores = %d\n", nCores + 1);//感觉这个是错的。
        }

        nCacheType = (CPUInfo[0] & 0x1f);
        nCacheLevel = (CPUInfo[0] & 0xe0) >> 5;
        bSelfInit = (CPUInfo[0] & 0x100) >> 8;
        bFullyAssociative = (CPUInfo[0] & 0x200) >> 9;
        nMaxThread = (CPUInfo[0] & 0x03ffc000) >> 14;
        nSysLineSize = (CPUInfo[1] & 0x0fff);
        nPhysicalLinePartitions = (CPUInfo[1] & 0x03ff000) >> 12;
        nWaysAssociativity = (CPUInfo[1]) >> 22;
        nNumberSets = CPUInfo[2];

        printf_s("\n");
        printf_s("ECX Index %d\n", Index);
        switch (nCacheType)
        {
        case 0:
            printf_s("   Type: Null\n");
            break;
        case 1:
            printf_s("   Type: Data Cache\n");
            break;
        case 2:
            printf_s("   Type: Instruction Cache\n");
            break;
        case 3:
            printf_s("   Type: Unified Cache\n");
            break;
        default:
            printf_s("   Type: Unknown\n");
        }

        printf_s("   Level = %d\n", nCacheLevel + 1);//感觉无须加一。INTEL说了:starts at 1。估计微软的人还认为:starts at 0。
        if (bSelfInit) {
            printf_s("   Self Initializing\n");
        }
        else {
            printf_s("   Not Self Initializing\n");
        }

        if (bFullyAssociative) {
            printf_s("   Is Fully Associatve\n");
        }
        else {
            printf_s("   Is Not Fully Associatve\n");
        }

        printf_s("   Max Threads = %d\n", nMaxThread + 1);
        //printf_s("   System Line Size = %d\n", nSysLineSize + 1);
        //printf_s("   Physical Line Partions = %d\n", nPhysicalLinePartitions + 1);
        //printf_s("   Ways of Associativity = %d\n", nWaysAssociativity + 1);
        //printf_s("   Number of Sets = %d\n", nNumberSets + 1);

        //微软网站上的几个CPUID例子是没有计算CPU cache大小的,如:https://msdn.microsoft.com/en-us/library/hskdteyh(v=vs.100).aspx ,这只是简单的列出值而已。

        //WRK如是说:
        // Cache size = Ways x Partitions x LineSize x Sets.
        // N.B. For fully-associative cache, the "Sets" returned from cpuid is actually the number of entries, not the "Ways".
        // Therefore the formula of evaluating the cache size below will still hold.

        /*
        INTEL如是说:

        INPUT EAX = 04H: Returns Deterministic Cache Parameters for Each Level

        When CPUID executes with EAX set to 04H and ECX contains an index value,
        the processor returns encoded data that describe a set of deterministic cache parameters (for the cache level associated with the input in ECX).
        Valid index values start from 0.

        Software can enumerate the deterministic cache parameters for each level of the cache hierarchy starting with an index value of 0,
        until the parameters report the value associated with the cache type field is 0.
        The architecturally defined fields reported by deterministic cache parameters are documented in Table 3-17.

        This Cache Size in Bytes
        = (Ways + 1) * (Partitions + 1) * (Line_Size + 1) * (Sets + 1)
        = (EBX[31:22] + 1) * (EBX[21:12] + 1) * (EBX[11:0] + 1) * (ECX + 1)

        The CPUID leaf 04H also reports data that can be used to derive the topology of processor cores in a physical package.
        This information is constant for all valid index values.
        Software can query the raw data reported by executing CPUID with EAX=04H and ECX=0 and use it as part of the topology enumeration algorithm described in Chapter 8,
        “Multiple-Processor Management,” in the Intel? 64 and IA-32 Architectures Software Developer’s Manual, Volume 3A.
        */

        CacheSize = (CacheInfoEbx.Associativity + 1) * (CacheInfoEbx.Partitions + 1) * (CacheInfoEbx.LineSize + 1) * (CPUInfo[2] + 1);

        ULONGLONG Cache_Size = (nWaysAssociativity + 1) * (nPhysicalLinePartitions + 1) * (nSysLineSize + 1) * (nNumberSets + 1);
        assert(Cache_Size == CacheSize);
        /*
        其实:
        nWaysAssociativity可命名为Ways
        nPhysicalLinePartitions可命名为Partitions
        nSysLineSize可命名为Line_Size
        nNumberSets可命名为Sets
        */

        if (CacheSize >= (1024 * 1024)) {
            printf_s("   CacheSize = %dMB.\n", CacheSize / (1024 * 1024));
        }
        else {
            printf_s("   CacheSize = %dKB.\n", CacheSize / 1024);
        }
    }

    return 0;
}

获取Intel CPU的温度

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

#include <intrin.h> //VS2012编译。
#include <immintrin.h>//VS2012编译。
//#include <mmintrin.h> //WDK 编译。
//#include <emmintrin.h>//WDK 编译。
//#include <xmmintrin.h>//WDK 编译。

#pragma warning(disable:4100)

/*
CPUID.06H:EAX[bit0]

19CH 412   IA32_THERM_STATUS Core Thermal Monitor Status (R/W) See Table 35-2.

1A2H 418   MSR_TEMPERATURE_TARGET Package
15:0  Reserved.
23:16 Temperature Target (R)
The default thermal throttling or PROCHOT# activation temperature in degree C,
The effective temperature for thermal throttling or PROCHOT# activation is “Temperature Target” +“Target Offset”
29:24 Target Offset (R/W)
Specifies an offset in degrees C to adjust the throttling and PROCHOT# activation temperature from the default target specified in TEMPERATURE_TARGET (bits 23:16).
*/


#define IA32_THERM_STATUS         0x19C
#define MSR_TEMPERATURE_TARGET    0x1A2


BOOL is_support_intel()
{
    BOOL B = FALSE;
    char CPUString[0x20];
    int CPUInfo[4] = { -1 };
    unsigned    nIds;

    // __cpuid with an InfoType argument of 0 returns the number of valid Ids in CPUInfo[0] and the CPU identification string in the other three array elements.
    // The CPU identification string is not in linear order.
    // The code below arranges the information in a human readable form.
    __cpuid(CPUInfo, 0);
    nIds = CPUInfo[0];
    memset(CPUString, 0, sizeof(CPUString));
    *((int*) CPUString) = CPUInfo[1];
    *((int*) (CPUString + 4)) = CPUInfo[3];
    *((int*) (CPUString + 8)) = CPUInfo[2];

    //printf_s("\n\nCPU String: %s\n", CPUString);//GenuineIntel
    if (_stricmp(CPUString, "GenuineIntel") == 0)
    {
        B = TRUE;
    }

    return B;
}


BOOL is_support_cpuid()
/*
判断CPU是否支持CPUID指令。
*/
{
    BOOL B = FALSE;
    SIZE_T eflags1;
    SIZE_T eflags2;

    eflags1 = __readeflags();
    __writeeflags(eflags1 | 0x200000);
    eflags2 = __readeflags();
    __writeeflags(eflags1);
    if (eflags1 == eflags2) {
        B = FALSE;
    }
    else {
        B = TRUE;
    }

    return B;
}


//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_SUCCESS;
    unsigned __int64 tt = 0;
    unsigned __int64 ts = 0;
    int x = 0;
    int to = 0;
    int i = 0;
    int CPUInfo[4] = { -1 };
    unsigned int t = 0;

    //KdBreakPoint();
    //__debugbreak();

    DriverObject->DriverUnload = Unload;

    //识别是否支持CPUID指令。
    if (!is_support_cpuid())
    {
        return 0;
    }
 
    //识别是不是Intel处理器。
    if (!is_support_intel())
    {
        return 0;
    }

    //识别是否支持查询CPU的温度。
    __cpuid(CPUInfo, 6);
    t = CPUInfo[0];
    //CPUID.06H:EAX[bit0] == 1
    //可是下面的两个值获取的都为零。

    tt = __readmsr(MSR_TEMPERATURE_TARGET);//如果这个数字为0,可以认为是在虚拟机中,有的软件叫:耐热,可能是极限。
    ts = __readmsr(IA32_THERM_STATUS);

    //KdPrint(("MSR_TEMPERATURE_TARGET:0x%x.\r\n", tt));
    //KdPrint(("IA32_THERM_STATUS:0x%x.\r\n", ts));

    x = tt & (0xFF0000);//23:16 Temperature Target (R)
    to = ts & (0x7F0000);//22:16 Digital Readout (RO)

    i = x - to;
    i = i / 0x10000;

    KdPrint(("TEMPERATURE:%d.\r\n", i));
    //这个数字和别的软件有1-2度的差别。
    //不过Core-Temp和hwmonitor也是相差1-2度。

    return status;
}

/*
注意:测试环境最好不要用虚拟机,用真实的物理机器。
如是64位Windows,可以开启WINDBG的本机内核调试:
lkd> rdmsr 0x19C; rdmsr 0x1A2
msr[19c] = 00000000`88470000
msr[1a2] = 00000000`00691400
再计算。

注意:每个核心有个温度,不是每个CPU线程有个温度。

具体的做法有:
1.创建个设备,提供个借口供应用层用。
2.开启个定时器,在内核不停的打印消息。


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

2016年2月1日星期一

WINDBG打印系统进程的线程信息

引子:可是在调试模式下查看64位的Windows内核和非调试模式下是不一样的,比如:系统进程的线程。
      如要做到这,可以用ARK,这大多是没有符号的。
      还可以用procexp.exe。
      还可以用windbg的命令:!process.
      但是我想在本地内核调试的模式下在windbg中看到类似于ARK/procexp.exe!process中显示的那样(甚至随心所欲)。
      简而言之:!process显示的线程信息太多,太丰富,我要精简,然后再细化。

   
经过分析得出:
lkd> !list -t nt!_LIST_ENTRY.Flink -x "dt nt!_ethread StartAddress Cid.UniqueThread @@(#CONTAINING_RECORD(@$extret, nt!_ethread, ThreadListEntry))" -m 3 (poi(nt!PsInitialSystemProcess) + @@(#FIELD_OFFSET(nt!_EPROCESS, ThreadListHead)))
   +0x390 StartAddress     : (null)
   +0x3b8 Cid              :
      +0x008 UniqueThread     : (null)

   +0x390 StartAddress     : 0xfffff800`0375f4f0 Void
   +0x3b8 Cid              :
      +0x008 UniqueThread     : 0x00000000`00000008 Void

   +0x390 StartAddress     : 0xfffff800`033f1960 Void
   +0x3b8 Cid              :
      +0x008 UniqueThread     : 0x00000000`0000000c Void
注意:这里只显示前3个,而且第一个是无效的。


不过我最终得出的是符号地址,这里没有,如:%y,dps,ln之类的信息。
所以要学习WINDBG脚本,脚本内容如下:

r $t0 = (poi(nt!PsInitialSystemProcess) + @@(#FIELD_OFFSET(nt!_EPROCESS, ThreadListHead)))

.printf /D "%y\n", @$t0;

.for (r $t1 = poi(@$t0); (@$t1 != 0) & (@$t1 != @$t0); r $t1 = poi(@$t1))
{
    r? $t2 = #CONTAINING_RECORD(@$t1, nt!_ETHREAD, ThreadListEntry);
 
    r? $t3 = @@c++(&@$t2->Cid.UniqueThread);
 
    .printf /D "UniqueThread:%d\t", poi(@$t3);
    .printf /D "ETHREAD:%p\t", @$t2;
    .printf /D "StartAddress:%y\n", poi(@@c++(&@$t2->StartAddress));
}

这里还可以用DML使每行高亮/链接,点击后运行个指令,如:!thread等。

运行结果如下:lkd> $$>< F:\windbg\PsInitialSystemProcess.txt
fffffa80`06752e18
UniqueThread:8 ETHREAD:fffffa8006752580 StartAddress:nt!Phase1Initialization (fffff800`0375f4f0)
UniqueThread:12 ETHREAD:fffffa80067583b0 StartAddress:nt!PopIrpWorkerControl (fffff800`033f1960)
UniqueThread:16 ETHREAD:fffffa8006759040 StartAddress:nt!PopIrpWorker (fffff800`033f12e0)
UniqueThread:20 ETHREAD:fffffa8006759b50 StartAddress:nt!PopIrpWorker (fffff800`033f12e0)
UniqueThread:24 ETHREAD:fffffa800675f850 StartAddress:nt!ExpWorkerThread (fffff800`032dc3a4)
UniqueThread:28 ETHREAD:fffffa800676ab50 StartAddress:nt!ExpWorkerThread (fffff800`032dc3a4)
UniqueThread:32 ETHREAD:fffffa800676a660 StartAddress:nt!ExpWorkerThread (fffff800`032dc3a4)
......


made by correy
made at 2016.02.01
http://correy.webs.com