Windows驱动开发学习记录- Hook NtCreateProcessEx/NtCreateUserProcess实现创建进程过滤
学习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)