Windows ObjectType Hook 之 ParseProcedure

发布时间 2023-11-02 11:22:55作者: 禁锢在时空之中的灵魂

1、背景

  Object Type Hook 是基于 Object Type的一种深入的 Hook,比起常用的 SSDT Hook 更为深入。

  有关 Object Type 的分析见文章 《Windows驱动开发学习记录-ObjectType Hook之ObjectType结构相关分析》

  这里进行的 Hook 为 其中之一的 ParseProcedure。文章实现注册表 Key 对象的过滤。

 

2、ParseProcedure 函数声明

  见文章 《Windows驱动开发学习记录-ObjectType Hook之ObjectType结构相关分析》

  这里取 x64 环境下结构:

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
    );

  在Win11 x64 环境下结构如下:

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,
    IN POB_EXTENDED_PARSE_PARAMETERS Paramters,  //Win 11上有这个参数 
    OUT PVOID *Object
    );

 

3、Key 对象过滤

3.1 实验目标

  这里实现对注册表 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run 键值过滤,达到不能打开该键的逻辑。

 

3.2 实现逻辑

  经过实验分析以及在小节 2 中的结构声明,对于路径的判断可以先用 ObQueryNameString 查询参数 ParseObject,得到一个路径。 

  但该路径并不一定是完整的全路径,需要再附加上参数 RemainingName 所对应的路径,加上该路径后的就是完整的全路径。

  Key对象的指针是导出的,可以直接使用,名称为 CmKeyObjectType, 也可以参考 《遍历Windows内核ObjectType》来获取。

 

3.3 实现代码

#if DBG
#define KDPRINT(projectName, format, ...) DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL,\
																						  projectName "::【" __FUNCTION__  "】" ##format, \
																						  ##__VA_ARGS__ ) 
#else
#define KDPRINT(format, ...)
#endif

typedef struct _OBJECT_TYPE_FLAGS {
        UCHAR CaseInsensitive : 1;
        UCHAR UnnamedObjectsOnly : 1;
        UCHAR UseDefaultObject : 1;
        UCHAR SecurityRequired : 1;
        UCHAR MaintainHandleCount : 1;
        UCHAR MaintainTypeList : 1;
        UCHAR SupportsObjectCallbacks : 1;
        UCHAR CacheAligned : 1;
}OBJECT_TYPE_FLAGS, * P_OBJECT_TYPE_FLAGS;

typedef struct _OB_EXTENDED_PARSE_PARAMETERS
{
        USHORT Length;
        ULONG  RestrictedAccessMask;
        PVOID Silo;
}OB_EXTENDED_PARSE_PARAMETERS, *POB_EXTENDED_PARSE_PARAMETERS;

typedef struct _OBJECT_TYPE_INITIALIZER {
        USHORT				wLength;
        OBJECT_TYPE_FLAGS	ObjectTypeFlags;
        ULONG				ObjcetTypeCode;
        ULONG				InvalidAttributes;
        GENERIC_MAPPING		GenericMapping;
        ULONG				ValidAccessMask;
        ULONG				RetainAccess;
        ULONG				PoolType;
        ULONG				DefaultPagedPoolCharge;
        ULONG				DefaultNonPagedPoolCharge;
        PVOID				DumpProcedure;
        PVOID				OpenProcedure;
        PVOID				CloseProcedure;
        PVOID				DeleteProcedure;
        union 
        {
                PVOID				ParseProcedure;
                PVOID				ParseProcedureEx;
        };
        
        PVOID				SecurityProcedure;
        PVOID				QueryNameProcedure;
        PVOID				OkayToCloseProcedure;
}OBJECT_TYPE_INITIALIZER, * POBJECT_TYPE_INITIALIZER;

typedef struct _OBJECT_TYPE_EX {
        LIST_ENTRY					TypeList;
        UNICODE_STRING				Name;
        ULONGLONG					DefaultObject;
        ULONG						Index;
        ULONG						TotalNumberOfObjects;
        ULONG						TotalNumberOfHandles;
        ULONG						HighWaterNumberOfObjects;
        ULONG						HighWaterNumberOfHandles;
        OBJECT_TYPE_INITIALIZER		TypeInfo;
        ULONGLONG					TypeLock;
        ULONG						Key;
        LIST_ENTRY					CallbackList;
}OBJECT_TYPE_EX, * POBJECT_TYPE_EX;


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

typedef
NTSTATUS
(NTAPI* PPARSE_PROCEDURE_EX)(
        IN PVOID ParseObject,
        IN POBJECT_TYPE ObjectType,
        IN OUT PACCESS_STATE AccessState,
        IN CHAR Flag,
        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,
        IN POB_EXTENDED_PARSE_PARAMETERS Paramters,  //Win 11上有这个参数 
        OUT PVOID* Object
        );

typedef struct _OBJECT_TYPE_HOOK_INFORMATION
{
        POBJECT_TYPE_EX pHookedObject;
        PPARSE_PROCEDURE_EX pOringinalParseProcedureAddress;
}OBJECT_TYPE_HOOK_INFORMATION, * POBJECT_TYPE_HOOK_INFORMATION;

OBJECT_TYPE_HOOK_INFORMATION g_HookInfomation = { 0 };
UNICODE_STRING g_usRunKeyName =
RTL_CONSTANT_STRING(L"*\\REGISTRY\\MACHINE\\SOFTWARE\\MICROSOFT\\WINDOWS\\CURRENTVERSION\\RUN*");
UNICODE_STRING g_usSeperator = RTL_CONSTANT_STRING(L"\\");
NTSTATUS
NTAPI
CustomKeyParseProcedure(
        IN PVOID ParseObject,
        IN POBJECT_TYPE ObjectType,
        IN OUT PACCESS_STATE AccessState,
        IN CHAR Flag,
        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,
       IN POB_EXTENDED_PARSE_PARAMETERS Paramters,
        OUT PVOID* Object)
{
        NTSTATUS ntStatus = STATUS_SUCCESS;
        ULONG ulRet = 0;
        BOOLEAN bFilterKey = false;

        if (ParseObject && (ObjectType == *CmKeyObjectType))
        {
                POBJECT_NAME_INFORMATION pName = (POBJECT_NAME_INFORMATION)ExAllocatePoolWithTag(
                        NonPagedPool, 1024, 'Mut');
                if (pName)
                {
                        ntStatus = ObQueryNameString(ParseObject, pName, 1024, &ulRet);
                        if (NT_SUCCESS(ntStatus))
                        {
                                //KDPRINT("【ObjectTypeHook】", "ParseObject is %wZ\r\n", &pName->Name);
                                if (RemainingName)
                                {
                                        //KDPRINT("【ObjectTypeHook】", "CompleteName is %wZ\r\n", CompleteName);
                                        //KDPRINT("【ObjectTypeHook】", "RemainingName is %wZ\r\n", RemainingName);
                                        pName->Name.MaximumLength = 1024 - sizeof(UNICODE_STRING);
                                        RtlAppendUnicodeStringToString(&pName->Name, &g_usSeperator);
                                        ntStatus = RtlAppendUnicodeStringToString(&pName->Name, RemainingName);
                            
                                        if (NT_SUCCESS(ntStatus))
                                        { 
                                                if (FsRtlIsNameInExpression(&g_usRunKeyName, &pName->Name, true, NULL))
                                                {
                                                        KDPRINT("【ObjectTypeHook】", "Need Filter Key Path Is %wZ\r\n", &pName->Name);
                                                        KDPRINT("【ObjectTypeHook】", "Denied Process Id is 0x%08d\r\n", PsGetCurrentProcessId());
                                                        bFilterKey = true;
                                                }
                                        }  
                                }     
                        }
                        ExFreePoolWithTag(pName, 'name');
                }
        }


        if (bFilterKey)
        {
                return STATUS_ACCESS_DENIED;
        }
        else
        {
                ntStatus = STATUS_SUCCESS;
                if (g_HookInfomation.pOringinalParseProcedureAddress)
                {
                        ntStatus = g_HookInfomation.pOringinalParseProcedureAddress(
                                ParseObject, ObjectType, AccessState, Flag, Attributes, CompleteName, RemainingName, Context,
                                SecurityQos, Paramters, Object);
                }

                return ntStatus;
        }

}

void UnHookObjectType()
{
        KDPRINT("【ObjectTypeHook】", "UnHook...\r\n");
        if (g_HookInfomation.pHookedObject)
        {
                InterlockedExchangePointer(
                        (PVOID*)(&g_HookInfomation.pHookedObject->TypeInfo.ParseProcedure),
                        g_HookInfomation.pOringinalParseProcedureAddress);
        }
}

VOID DriverUnload(PDRIVER_OBJECT pDriverObject)
{
        UNREFERENCED_PARAMETER(pDriverObject);
        KDPRINT("【ObjectTypeHook】", "CurrentProcessId : 0x%p CurrentIRQL : 0x%u \r\n",
                PsGetCurrentProcessId(),
                KeGetCurrentIrql());
        UnHookObjectType();
}


EXTERN_C NTSTATUS  DriverEntry(PDRIVER_OBJECT pDriverObject,
        PUNICODE_STRING pRegistryPath)
{
        UNREFERENCED_PARAMETER(pDriverObject);
        UNREFERENCED_PARAMETER(pRegistryPath);
        NTSTATUS ntStatus = STATUS_SUCCESS;
        KDPRINT("【ObjectTypeHook】", " Hello Kernel World! CurrentProcessId:0x%p CurrentIRQL:0x%u\r\n",
                PsGetCurrentProcessId(),
                KeGetCurrentIrql());
        pDriverObject->DriverUnload = DriverUnload;
        g_HookInfomation.pHookedObject = (POBJECT_TYPE_EX)(*CmKeyObjectType);
        g_HookInfomation.pOringinalParseProcedureAddress =
                (PPARSE_PROCEDURE_EX)(((POBJECT_TYPE_EX)(*CmKeyObjectType))->TypeInfo.ParseProcedure);
        InterlockedExchangePointer(
                (PVOID*)(&g_HookInfomation.pHookedObject->TypeInfo.ParseProcedure),
                CustomKeyParseProcedure);
        return ntStatus;
}

 

3.4 实现效果

  安装驱动后去打开注册表 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run 键,效果如下: