在笔者上一篇文章《内核枚举Registry注册表回调》
中我们通过特征码定位实现了对注册表回调的枚举,本篇文章LyShark
将教大家如何枚举系统中的ProcessObCall
进程回调以及ThreadObCall
线程回调,之所以放在一起来讲解是因为这两中回调在枚举是都需要使用通用结构体_OB_CALLBACK
以及_OBJECT_TYPE
所以放在一起来讲解最好不过。
进程与线程ObCall回调是Windows操作系统提供的一种机制,它允许开发者在进程或线程发生创建、销毁、访问、修改等事件时拦截并处理这些事件。进程与线程ObCall回调是通过操作系统提供的回调机制来实现的。
当操作系统创建、销毁、访问或修改进程或线程时,它会触发进程与线程ObCall回调事件,然后在回调事件中调用注册的进程与线程ObCall回调函数。开发者可以在进程与线程ObCall回调函数中执行自定义的逻辑,例如记录日志,过滤敏感数据,或者阻止某些操作。
进程与线程ObCall回调可以通过操作系统提供的回调函数PsSetCreateProcessNotifyRoutine、PsSetCreateThreadNotifyRoutine、PsSetLoadImageNotifyRoutine等来进行注册。同时,进程与线程ObCall回调函数需要遵守一定的约束条件,例如不能阻塞或挂起进程或线程的创建或访问,不能调用一些内核API函数等。
进程与线程ObCall回调在安全软件、系统监控和调试工具等领域有着广泛的应用。
我们来看一款闭源ARK工具是如何实现的:
首先我们需要定义好结构体,结构体是微软公开的,如果有其它需要请自行去微软官方去查。
typedef struct _OBJECT_TYPE_INITIALIZER
{USHORT Length; // Uint2BUCHAR ObjectTypeFlags; // UCharULONG ObjectTypeCode; // Uint4BULONG InvalidAttributes; // Uint4BGENERIC_MAPPING GenericMapping; // _GENERIC_MAPPINGULONG ValidAccessMask; // Uint4BULONG RetainAccess; // Uint4BPOOL_TYPE PoolType; // _POOL_TYPEULONG DefaultPagedPoolCharge; // Uint4BULONG DefaultNonPagedPoolCharge; // Uint4BPVOID DumpProcedure; // Ptr64 voidPVOID OpenProcedure; // Ptr64 longPVOID CloseProcedure; // Ptr64 voidPVOID DeleteProcedure; // Ptr64 voidPVOID ParseProcedure; // Ptr64 longPVOID SecurityProcedure; // Ptr64 longPVOID QueryNameProcedure; // Ptr64 longPVOID OkayToCloseProcedure; // Ptr64 unsigned charULONG WaitObjectFlagMask; // Uint4BUSHORT WaitObjectFlagOffset; // Uint2BUSHORT WaitObjectPointerOffset; // Uint2B
}OBJECT_TYPE_INITIALIZER, *POBJECT_TYPE_INITIALIZER;typedef struct _OBJECT_TYPE
{LIST_ENTRY TypeList; // _LIST_ENTRYUNICODE_STRING Name; // _UNICODE_STRINGPVOID DefaultObject; // Ptr64 VoidUCHAR Index; // UCharULONG TotalNumberOfObjects; // Uint4BULONG TotalNumberOfHandles; // Uint4BULONG HighWaterNumberOfObjects; // Uint4BULONG HighWaterNumberOfHandles; // Uint4BOBJECT_TYPE_INITIALIZER TypeInfo; // _OBJECT_TYPE_INITIALIZEREX_PUSH_LOCK TypeLock; // _EX_PUSH_LOCKULONG Key; // Uint4BLIST_ENTRY CallbackList; // _LIST_ENTRY
}OBJECT_TYPE, *POBJECT_TYPE;#pragma pack(1)
typedef struct _OB_CALLBACK
{LIST_ENTRY ListEntry;ULONGLONG Unknown;HANDLE ObHandle;PVOID ObTypeAddr;PVOID PreCall;PVOID PostCall;
}OB_CALLBACK, *POB_CALLBACK;
#pragma pack()
代码部分的实现很容易,由于进程与线程句柄
的枚举很容易,直接通过(POBJECT_TYPE)(*PsProcessType))->CallbackList
就可以拿到链表头结构,得到后将其解析为POB_CALLBACK
并循环输出即可。
#include <ntifs.h>
#include <wdm.h>
#include <ntddk.h>typedef struct _OBJECT_TYPE_INITIALIZER
{USHORT Length; // Uint2BUCHAR ObjectTypeFlags; // UCharULONG ObjectTypeCode; // Uint4BULONG InvalidAttributes; // Uint4BGENERIC_MAPPING GenericMapping; // _GENERIC_MAPPINGULONG ValidAccessMask; // Uint4BULONG RetainAccess; // Uint4BPOOL_TYPE PoolType; // _POOL_TYPEULONG DefaultPagedPoolCharge; // Uint4BULONG DefaultNonPagedPoolCharge; // Uint4BPVOID DumpProcedure; // Ptr64 voidPVOID OpenProcedure; // Ptr64 longPVOID CloseProcedure; // Ptr64 voidPVOID DeleteProcedure; // Ptr64 voidPVOID ParseProcedure; // Ptr64 longPVOID SecurityProcedure; // Ptr64 longPVOID QueryNameProcedure; // Ptr64 longPVOID OkayToCloseProcedure; // Ptr64 unsigned charULONG WaitObjectFlagMask; // Uint4BUSHORT WaitObjectFlagOffset; // Uint2BUSHORT WaitObjectPointerOffset; // Uint2B
}OBJECT_TYPE_INITIALIZER, *POBJECT_TYPE_INITIALIZER;typedef struct _OBJECT_TYPE
{LIST_ENTRY TypeList; // _LIST_ENTRYUNICODE_STRING Name; // _UNICODE_STRINGPVOID DefaultObject; // Ptr64 VoidUCHAR Index; // UCharULONG TotalNumberOfObjects; // Uint4BULONG TotalNumberOfHandles; // Uint4BULONG HighWaterNumberOfObjects; // Uint4BULONG HighWaterNumberOfHandles; // Uint4BOBJECT_TYPE_INITIALIZER TypeInfo; // _OBJECT_TYPE_INITIALIZEREX_PUSH_LOCK TypeLock; // _EX_PUSH_LOCKULONG Key; // Uint4BLIST_ENTRY CallbackList; // _LIST_ENTRY
}OBJECT_TYPE, *POBJECT_TYPE;#pragma pack(1)
typedef struct _OB_CALLBACK
{LIST_ENTRY ListEntry;ULONGLONG Unknown;HANDLE ObHandle;PVOID ObTypeAddr;PVOID PreCall;PVOID PostCall;
}OB_CALLBACK, *POB_CALLBACK;
#pragma pack()VOID DriverUnload(PDRIVER_OBJECT pDriverObject)
{
}NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegPath)
{NTSTATUS status = STATUS_SUCCESS;DbgPrint("hello lyshark \n");POB_CALLBACK pObCallback = NULL;// 直接获取 CallbackList 链表LIST_ENTRY CallbackList = ((POBJECT_TYPE)(*PsProcessType))->CallbackList;// 开始遍历pObCallback = (POB_CALLBACK)CallbackList.Flink;do{if (FALSE == MmIsAddressValid(pObCallback)){break;}if (NULL != pObCallback->ObHandle){// 显示DbgPrint("[lyshark] ObHandle = %p | PreCall = %p | PostCall = %p \n", pObCallback->ObHandle, pObCallback->PreCall, pObCallback->PostCall);}// 获取下一链表信息pObCallback = (POB_CALLBACK)pObCallback->ListEntry.Flink;} while (CallbackList.Flink != (PLIST_ENTRY)pObCallback);return status;
}
运行这段驱动程序,即可得到进程句柄
回调:
当然了如上是进程句柄
的枚举,如果是想要输出线程句柄,则只需要替换代码中的PsProcessType
为((POBJECT_TYPE)(*PsThreadType))->CallbackList
即可,修改后的代码如下。
#include <ntifs.h>
#include <wdm.h>
#include <ntddk.h>typedef struct _OBJECT_TYPE_INITIALIZER
{USHORT Length; // Uint2BUCHAR ObjectTypeFlags; // UCharULONG ObjectTypeCode; // Uint4BULONG InvalidAttributes; // Uint4BGENERIC_MAPPING GenericMapping; // _GENERIC_MAPPINGULONG ValidAccessMask; // Uint4BULONG RetainAccess; // Uint4BPOOL_TYPE PoolType; // _POOL_TYPEULONG DefaultPagedPoolCharge; // Uint4BULONG DefaultNonPagedPoolCharge; // Uint4BPVOID DumpProcedure; // Ptr64 voidPVOID OpenProcedure; // Ptr64 longPVOID CloseProcedure; // Ptr64 voidPVOID DeleteProcedure; // Ptr64 voidPVOID ParseProcedure; // Ptr64 longPVOID SecurityProcedure; // Ptr64 longPVOID QueryNameProcedure; // Ptr64 longPVOID OkayToCloseProcedure; // Ptr64 unsigned charULONG WaitObjectFlagMask; // Uint4BUSHORT WaitObjectFlagOffset; // Uint2BUSHORT WaitObjectPointerOffset; // Uint2B
}OBJECT_TYPE_INITIALIZER, *POBJECT_TYPE_INITIALIZER;typedef struct _OBJECT_TYPE
{LIST_ENTRY TypeList; // _LIST_ENTRYUNICODE_STRING Name; // _UNICODE_STRINGPVOID DefaultObject; // Ptr64 VoidUCHAR Index; // UCharULONG TotalNumberOfObjects; // Uint4BULONG TotalNumberOfHandles; // Uint4BULONG HighWaterNumberOfObjects; // Uint4BULONG HighWaterNumberOfHandles; // Uint4BOBJECT_TYPE_INITIALIZER TypeInfo; // _OBJECT_TYPE_INITIALIZEREX_PUSH_LOCK TypeLock; // _EX_PUSH_LOCKULONG Key; // Uint4BLIST_ENTRY CallbackList; // _LIST_ENTRY
}OBJECT_TYPE, *POBJECT_TYPE;#pragma pack(1)
typedef struct _OB_CALLBACK
{LIST_ENTRY ListEntry;ULONGLONG Unknown;HANDLE ObHandle;PVOID ObTypeAddr;PVOID PreCall;PVOID PostCall;
}OB_CALLBACK, *POB_CALLBACK;
#pragma pack()VOID DriverUnload(PDRIVER_OBJECT pDriverObject)
{
}NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegPath)
{NTSTATUS status = STATUS_SUCCESS;DbgPrint("hello lyshark \n");POB_CALLBACK pObCallback = NULL;// 直接获取 CallbackList 链表LIST_ENTRY CallbackList = ((POBJECT_TYPE)(*PsThreadType))->CallbackList;// 开始遍历pObCallback = (POB_CALLBACK)CallbackList.Flink;do{if (FALSE == MmIsAddressValid(pObCallback)){break;}if (NULL != pObCallback->ObHandle){// 显示DbgPrint("[LyShark] ObHandle = %p | PreCall = %p | PostCall = %p \n", pObCallback->ObHandle, pObCallback->PreCall, pObCallback->PostCall);}// 获取下一链表信息pObCallback = (POB_CALLBACK)pObCallback->ListEntry.Flink;} while (CallbackList.Flink != (PLIST_ENTRY)pObCallback);return status;
}
运行这段驱动程序,即可得到线程句柄
回调: