学习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)