学习SSDT Hook、SSDT Inline Hook的一点记录。

1.实现前提

        使用常规的SSDT hook或者 SSDT inline hook进行挂钩,这里只讨论过滤函数的实现和调试中的一些问题。测试环境为XP sp3 x86 和 Win7 SP1 x86。

 

 2.两个系统的差别

        Win7以上的系统环境,在后来系统中创建的进程的是NtCreateUserProcess,之前一直挂钩NtCreateProcessEx并不起作用,这里需要注意一下。

 

3.NtCreateUserProcess挂钩实现

3.1原型

 

NTSTATUS
NTAPI
NtCreateUserProcess(
        OUT PHANDLE ProcessHandle,
        OUT PHANDLE ThreadHandle,
        IN ACCESS_MASK ProcessDesiredAccess,
        IN ACCESS_MASK ThreadDesiredAccess,
        IN POBJECT_ATTRIBUTES ProcessObjectAttributes OPTIONAL,
        IN POBJECT_ATTRIBUTES ThreadObjectAttributes OPTIONAL,
        IN ULONG CreateProcessFlags,
        IN ULONG CreateThreadFlags,
        IN PRTL_USER_PROCESS_PARAMETERS ProcessParameters,
        IN PVOID Parameter9,
        IN PNT_PROC_THREAD_ATTRIBUTE_LIST AttributeList);

  其相关结构体定义如下:

typedef struct _RTL_USER_PROCESS_PARAMETERS {
        BYTE           Reserved1[16];
        PVOID          Reserved2[10];
        UNICODE_STRING ImagePathName;
        UNICODE_STRING CommandLine;
} RTL_USER_PROCESS_PARAMETERS, * PRTL_USER_PROCESS_PARAMETERS;
 
typedef struct _NT_PROC_THREAD_ATTRIBUTE_ENTRY {
        ULONG Attribute;    // PROC_THREAD_ATTRIBUTE_XXX,参见MSDN中UpdateProcThreadAttribute的说明
        SIZE_T Size;        // Value的大小
        ULONG_PTR Value;    // 保存4字节数据(比如一个Handle)或数据指针
        ULONG Unknown;      // 总是0,可能是用来返回数据给调用者
} PROC_THREAD_ATTRIBUTE_ENTRY, * PPROC_THREAD_ATTRIBUTE_ENTRY;
 
typedef struct _NT_PROC_THREAD_ATTRIBUTE_LIST {
        ULONG Length;       // 结构总大小
        PROC_THREAD_ATTRIBUTE_ENTRY Entry[1];
} NT_PROC_THREAD_ATTRIBUTE_LIST, * PNT_PROC_THREAD_ATTRIBUTE_LIST;

 

3.2代码实现

  实现就比较简单,可以通过 ProcessParameters中的ImagePathName来判断进程路径名

NTSTATUS
NTAPI
CustomNtCreateUserProcess(
        OUT PHANDLE ProcessHandle,
        OUT PHANDLE ThreadHandle,
        IN ACCESS_MASK ProcessDesiredAccess,
        IN ACCESS_MASK ThreadDesiredAccess,
        IN POBJECT_ATTRIBUTES ProcessObjectAttributes OPTIONAL,
        IN POBJECT_ATTRIBUTES ThreadObjectAttributes OPTIONAL,
        IN ULONG CreateProcessFlags,
        IN ULONG CreateThreadFlags,
        IN PRTL_USER_PROCESS_PARAMETERS ProcessParameters,
        IN PVOID Parameter9,
        IN PNT_PROC_THREAD_ATTRIBUTE_LIST AttributeList)
{
        DECLARE_CUSTOM_PROCEDURE_INFO(CustomNtCreateUserProcess, NtCreateUserProcessPtr);
        REFERENCE_COUNT_INCREMENT();
        NTSTATUS ntStatus = STATUS_UNSUCCESSFUL;
        UNICODE_STRING usFilterFile = RTL_CONSTANT_STRING(STRING_FILTER_CREATE_PROCESS_NAME);
 
        do
        {
                if (ProcessParameters)
                {
                        PUNICODE_STRING pImagePathName = &ProcessParameters->ImagePathName;
                        if (FsRtlIsNameInExpression(&usFilterFile, pImagePathName, true, NULL))
                        {
                                KDPRINT("【SSDTInlineHook】", "Found Target Create Process: %wZ\r\n", pImagePathName);
                                KDPRINT("【SSDTInlineHook】", "Access Denied\r\n");
                                ntStatus = STATUS_ACCESS_DENIED;
                                break;
                        }
                }
 
                ntStatus = OriginalNtCreateUserProcessPtr(
                        ProcessHandle,
                        ThreadHandle,
                        ProcessDesiredAccess,
                        ThreadDesiredAccess,
                        ProcessObjectAttributes,
                        ThreadObjectAttributes,
                        CreateProcessFlags,
                        CreateThreadFlags,
                        ProcessParameters,
                        Parameter9,
                        AttributeList);
 
        } while (false);
        REFERENCE_COUNT_DECREMENT();
        return ntStatus;
}

 

4.NtCreateProcessEx挂钩实现

4.1原型

NTSTATUS
NTAPI
CustomNtCreateProcessEx(
        OUT PHANDLE ProcessHandle,
        IN ACCESS_MASK DesiredAccess,
        IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
        IN HANDLE ParentProcess,
        IN BOOLEAN InheritObjectTable,
        IN HANDLE SectionHandle OPTIONAL,
        IN HANDLE DebugPort OPTIONAL,
        IN HANDLE ExceptionPort OPTIONAL,
        IN BOOLEAN InJob)

 

    其中SectionHandle对应的就是创建进程的内存映射句柄,通过这个参数来获取进程的路径或者其它信息。

        相关结构体如下(其中有一处通过Windbg调试出来的结构并不正确):

typedef struct _CONTROL_AREA {
        PVOID Segment;
        LIST_ENTRY DereferenceList;
        ULONG NumberOfSectionReferences;
        ULONG NumberOfPfnReferences;
        ULONG NumberOfMappedViews;
        USHORT NumberOfSubsections;
        USHORT FlushInProgressCount;
        ULONG NumberOfUserReferences;
        PVOID u;
        PFILE_OBJECT FilePointer;
        PVOID WaitingForDeletion;
        USHORT ModifiedWriteCount;
        USHORT NumberOfSystemCacheViews;
}CONTROL_AREA, *PCONTROL_AREA;
 
typedef struct _SEGMENT_OBJECT
{
        PCONTROL_AREA ControlArea;
        ULONG TotalNumberOfPtes;
        LARGE_INTEGER SizeOfSegment;
        ULONG  NonExtendedPtes;
        ULONG ImageCommitment;
        PVOID BaseAddress;
        PVOID   Subsection;
        PVOID LargeControlArea;
        PVOID MmSectionFlags;
        PVOID MmSubSectionFlags;
}SEGMENT_OBJECT, * PSEGMENT_OBJECT;
 
typedef struct _SECTION_OBJECT
{
        PVOID StartingVa;
        PVOID EndingVa;
        PVOID Parent;
        PVOID LeftChild;
        PVOID RightChild;
        PSEGMENT_OBJECT Segment;
}SECTION_OBJECT, * PSECTION_OBJECT;

      其中_SEGMENT_OBJECT在Win XP中输出的结构为以下:

0: kd> dt _SEGMENT_OBJECT
nt!_SEGMENT_OBJECT
   +0x000 BaseAddress      : Ptr32 Void
   +0x004 TotalNumberOfPtes : Uint4B
   +0x008 SizeOfSegment    : _LARGE_INTEGER
   +0x010 NonExtendedPtes  : Uint4B
   +0x014 ImageCommitment  : Uint4B
   +0x018 ControlArea      : Ptr32 _CONTROL_AREA
   +0x01c Subsection       : Ptr32 _SUBSECTION
   +0x020 LargeControlArea : Ptr32 _LARGE_CONTROL_AREA
   +0x024 MmSectionFlags   : Ptr32 _MMSECTION_FLAGS
   +0x028 MmSubSectionFlags : Ptr32 _MMSUBSECTION_FLAGS

 

  经过调试发现其中的BaseAddress和ControlArea似乎弄反了,以至于最后取的数据始终不对,因此我将结构定义中的错误部分做了相应调整。

  可以发现获取文件信息,比如文件路径可以通过以下流程:

  ObReferenceObjectByHandle(SectionHandle)->SECTION_OBJECT->Segment->ControlArea->FilePointer,拿到文件指针就可以作相应的操作了,比如获取文件全路径。

 

4.2实现

 

.h文件:

#define STRING_FILTER_CREATE_PROCESS_NAME        (L"*测试*.EXE")
 
#define ExFreePoolSafe(x)       \
                if(x != NULL){\
                        ExFreePool(x);\
                        x = NULL;\
                }
 
EXTERN_C
NTKERNELAPI
POBJECT_TYPE MmSectionObjectType;

 

.cpp文件

NTSTATUS
NTAPI
CustomNtCreateProcessEx(
        OUT PHANDLE ProcessHandle,
        IN ACCESS_MASK DesiredAccess,
        IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
        IN HANDLE ParentProcess,
        IN BOOLEAN InheritObjectTable,
        IN HANDLE SectionHandle OPTIONAL,
        IN HANDLE DebugPort OPTIONAL,
        IN HANDLE ExceptionPort OPTIONAL,
        IN BOOLEAN InJob)
{
        DECLARE_CUSTOM_PROCEDURE_INFO(CustomNtCreateProcessEx, NtCreateProcessExPtr);
        REFERENCE_COUNT_INCREMENT();
        NTSTATUS ntStatus = STATUS_UNSUCCESSFUL;
        UNICODE_STRING usFilterFile = RTL_CONSTANT_STRING(STRING_FILTER_CREATE_PROCESS_NAME);
        PSECTION_OBJECT pSectionObject = NULL;
        PSEGMENT_OBJECT pSegmentObject = NULL;
        PCONTROL_AREA pControlArea = NULL;
        PFILE_OBJECT pFileObject = NULL;
        POBJECT_NAME_INFORMATION pObjectNameInformation = NULL;
        do
        {
                if (SectionHandle)
                {
                        ntStatus = ObReferenceObjectByHandle(
                                SectionHandle,
                                SECTION_MAP_EXECUTE,
                                MmSectionObjectType,
                                ExGetPreviousMode(),
                                (PVOID*)&pSectionObject,
                                NULL);
                        if (!NT_SUCCESS(ntStatus))
                        {
                                KDPRINT("【SSDTInlineHook】", "ObReferenceObjectByHandle Error Code,0x%p\r\n", ntStatus);
                        }
                        else
                        {
                                KDPRINT("【SSDTInlineHook】", "ObReferenceObjectByHandle Successfully\r\n");
                                __try
                                {
                                        
                                        pSegmentObject = pSectionObject->Segment;
                                        pControlArea = pSegmentObject->ControlArea;
                                        pFileObject = pControlArea->FilePointer;
 
                                        ntStatus = IoQueryFileDosDeviceName(pFileObject, &pObjectNameInformation);
                                        if (NT_SUCCESS(ntStatus))
                                        {
                                                PUNICODE_STRING pUsObjectName = &pObjectNameInformation->Name;
 
                                                if (FsRtlIsNameInExpression(&usFilterFile, pUsObjectName, true, NULL))
                                                {
                                                        KDPRINT("【SSDTInlineHook】", "Found Target Create Process: %wZ\r\n", pUsObjectName);
                                                        KDPRINT("【SSDTInlineHook】", "Access Denied\r\n");
                                                        ntStatus = STATUS_SILENT_RETURN;
                                                        break;
                                                }
                                        }
                                        else
                                        {
                                                KDPRINT("【SSDTInlineHook】", "IoQueryFileDosDeviceName Failed\r\n");
                                        }
 
                                }
                                __except (EXCEPTION_EXECUTE_HANDLER)
                                {
                                        KDPRINT("【SSDTInlineHook】", "Section Access Failed\r\n");
                                }
                        }
                }
 
                ntStatus = OriginalNtCreateProcessExPtr(
                        ProcessHandle,
                        DesiredAccess,
                        ObjectAttributes,
                        ParentProcess,
                        InheritObjectTable,
                        SectionHandle,
                        DebugPort,
                        ExceptionPort,
                        InJob);
 
        } while (false);
 
        ExFreePoolSafe(pObjectNameInformation);
        REFERENCE_COUNT_DECREMENT();
 
        return ntStatus;
}

 

4.3关于MmSectionObjectType

  在调用ObReferenceObjectByHandle时传入的类型为MmSectionObjectType,这个类型类型WDK里并没有声明,但在内核中已经导出,所以我们声明一下就可以使用了。但这里我也遇到了一些问题。常见的一些内核对象类型wdm.h头文件里已经定义,如下:  

extern POBJECT_TYPE *CmKeyObjectType;
extern POBJECT_TYPE *IoFileObjectType;
extern POBJECT_TYPE *ExEventObjectType;
extern POBJECT_TYPE *ExSemaphoreObjectType;
extern POBJECT_TYPE *TmTransactionManagerObjectType;
extern POBJECT_TYPE *TmResourceManagerObjectType;
extern POBJECT_TYPE *TmEnlistmentObjectType;
extern POBJECT_TYPE *TmTransactionObjectType;
extern POBJECT_TYPE *PsProcessType;
extern POBJECT_TYPE *PsThreadType;
extern POBJECT_TYPE *PsJobType;
extern POBJECT_TYPE *SeTokenObjectType;

  

  我们使用的时候一般为 *IoFileObjectType或者*PsProcessType等等,是带有*号的。但根据我的测试发现在使用导出的内核对象类型和使用时是不能带*号的,也就是不是指针类型,不然调用ObReferenceObjectByHandle会返回不匹配的内核对象类型。所以使用时如实现代码,声明时如下:

EXTERN_C
NTKERNELAPI
POBJECT_TYPE MmSectionObjectType;

 

4.4 关于返回值

    一般过滤我们返回就是拒绝访问等,代码如STATUS_ACCESS_DENIED,但这里我返回的一个错误代码,内核中是错误代码,但解析到应用层就是成功,表现出来的效果就是静默错误,没有任何提示。错误代码如下:

#define STATUS_SILENT_RETURN            (0x80070000)

 

  关于情况说明可以参考<<Windows驱动开发学习记录-在内核中返回错误但用户层不提示错误>>

 

5.实现效果(WinXP x86)

NT_STATUS_ACCESS_DENIED列表_数据