6.9 Windows驱动开发:内核枚举进线程ObCall回调

本文涉及的产品
实时计算 Flink 版,5000CU*H 3个月
简介: 在笔者上一篇文章`《内核枚举Registry注册表回调》`中我们通过特征码定位实现了对注册表回调的枚举,本篇文章`LyShark`将教大家如何枚举系统中的`ProcessObCall`进程回调以及`ThreadObCall`线程回调,之所以放在一起来讲解是因为这两中回调在枚举是都需要使用通用结构体`_OB_CALLBACK`以及`_OBJECT_TYPE`所以放在一起来讲解最好不过。

在笔者上一篇文章《内核枚举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;                // Uint2B
    UCHAR ObjectTypeFlags;            // UChar
    ULONG ObjectTypeCode;             // Uint4B
    ULONG InvalidAttributes;          // Uint4B
    GENERIC_MAPPING GenericMapping;   // _GENERIC_MAPPING
    ULONG ValidAccessMask;       // Uint4B
    ULONG RetainAccess;         // Uint4B
    POOL_TYPE PoolType;        // _POOL_TYPE
    ULONG DefaultPagedPoolCharge;  // Uint4B
    ULONG DefaultNonPagedPoolCharge; // Uint4B
    PVOID DumpProcedure;       // Ptr64     void
    PVOID OpenProcedure;      // Ptr64     long
    PVOID CloseProcedure;     // Ptr64     void
    PVOID DeleteProcedure;        // Ptr64     void
    PVOID ParseProcedure;     // Ptr64     long
    PVOID SecurityProcedure;      // Ptr64     long
    PVOID QueryNameProcedure;     // Ptr64     long
    PVOID OkayToCloseProcedure;     // Ptr64     unsigned char
    ULONG WaitObjectFlagMask;     // Uint4B
    USHORT WaitObjectFlagOffset;    // Uint2B
    USHORT WaitObjectPointerOffset;   // Uint2B
}OBJECT_TYPE_INITIALIZER, *POBJECT_TYPE_INITIALIZER;

typedef struct _OBJECT_TYPE
{
   
   
    LIST_ENTRY TypeList;           // _LIST_ENTRY
    UNICODE_STRING Name;         // _UNICODE_STRING
    PVOID DefaultObject;         // Ptr64 Void
    UCHAR Index;             // UChar
    ULONG TotalNumberOfObjects;      // Uint4B
    ULONG TotalNumberOfHandles;      // Uint4B
    ULONG HighWaterNumberOfObjects;    // Uint4B
    ULONG HighWaterNumberOfHandles;    // Uint4B
    OBJECT_TYPE_INITIALIZER TypeInfo;  // _OBJECT_TYPE_INITIALIZER
    EX_PUSH_LOCK TypeLock;         // _EX_PUSH_LOCK
    ULONG Key;                 // Uint4B
    LIST_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;                // Uint2B
    UCHAR ObjectTypeFlags;            // UChar
    ULONG ObjectTypeCode;             // Uint4B
    ULONG InvalidAttributes;          // Uint4B
    GENERIC_MAPPING GenericMapping;   // _GENERIC_MAPPING
    ULONG ValidAccessMask;       // Uint4B
    ULONG RetainAccess;         // Uint4B
    POOL_TYPE PoolType;        // _POOL_TYPE
    ULONG DefaultPagedPoolCharge;  // Uint4B
    ULONG DefaultNonPagedPoolCharge; // Uint4B
    PVOID DumpProcedure;       // Ptr64     void
    PVOID OpenProcedure;      // Ptr64     long
    PVOID CloseProcedure;     // Ptr64     void
    PVOID DeleteProcedure;        // Ptr64     void
    PVOID ParseProcedure;     // Ptr64     long
    PVOID SecurityProcedure;      // Ptr64     long
    PVOID QueryNameProcedure;     // Ptr64     long
    PVOID OkayToCloseProcedure;     // Ptr64     unsigned char
    ULONG WaitObjectFlagMask;     // Uint4B
    USHORT WaitObjectFlagOffset;    // Uint2B
    USHORT WaitObjectPointerOffset;   // Uint2B
}OBJECT_TYPE_INITIALIZER, *POBJECT_TYPE_INITIALIZER;

typedef struct _OBJECT_TYPE
{
   
   
    LIST_ENTRY TypeList;           // _LIST_ENTRY
    UNICODE_STRING Name;         // _UNICODE_STRING
    PVOID DefaultObject;         // Ptr64 Void
    UCHAR Index;             // UChar
    ULONG TotalNumberOfObjects;      // Uint4B
    ULONG TotalNumberOfHandles;      // Uint4B
    ULONG HighWaterNumberOfObjects;    // Uint4B
    ULONG HighWaterNumberOfHandles;    // Uint4B
    OBJECT_TYPE_INITIALIZER TypeInfo;  // _OBJECT_TYPE_INITIALIZER
    EX_PUSH_LOCK TypeLock;         // _EX_PUSH_LOCK
    ULONG Key;                 // Uint4B
    LIST_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;                // Uint2B
    UCHAR ObjectTypeFlags;            // UChar
    ULONG ObjectTypeCode;             // Uint4B
    ULONG InvalidAttributes;          // Uint4B
    GENERIC_MAPPING GenericMapping;   // _GENERIC_MAPPING
    ULONG ValidAccessMask;       // Uint4B
    ULONG RetainAccess;         // Uint4B
    POOL_TYPE PoolType;        // _POOL_TYPE
    ULONG DefaultPagedPoolCharge;  // Uint4B
    ULONG DefaultNonPagedPoolCharge; // Uint4B
    PVOID DumpProcedure;       // Ptr64     void
    PVOID OpenProcedure;      // Ptr64     long
    PVOID CloseProcedure;     // Ptr64     void
    PVOID DeleteProcedure;        // Ptr64     void
    PVOID ParseProcedure;     // Ptr64     long
    PVOID SecurityProcedure;      // Ptr64     long
    PVOID QueryNameProcedure;     // Ptr64     long
    PVOID OkayToCloseProcedure;     // Ptr64     unsigned char
    ULONG WaitObjectFlagMask;     // Uint4B
    USHORT WaitObjectFlagOffset;    // Uint2B
    USHORT WaitObjectPointerOffset;   // Uint2B
}OBJECT_TYPE_INITIALIZER, *POBJECT_TYPE_INITIALIZER;

typedef struct _OBJECT_TYPE
{
   
   
    LIST_ENTRY TypeList;           // _LIST_ENTRY
    UNICODE_STRING Name;         // _UNICODE_STRING
    PVOID DefaultObject;         // Ptr64 Void
    UCHAR Index;             // UChar
    ULONG TotalNumberOfObjects;      // Uint4B
    ULONG TotalNumberOfHandles;      // Uint4B
    ULONG HighWaterNumberOfObjects;    // Uint4B
    ULONG HighWaterNumberOfHandles;    // Uint4B
    OBJECT_TYPE_INITIALIZER TypeInfo;  // _OBJECT_TYPE_INITIALIZER
    EX_PUSH_LOCK TypeLock;         // _EX_PUSH_LOCK
    ULONG Key;                 // Uint4B
    LIST_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;
}

运行这段驱动程序,即可得到线程句柄回调:

相关实践学习
基于Hologres轻松玩转一站式实时仓库
本场景介绍如何利用阿里云MaxCompute、实时计算Flink和交互式分析服务Hologres开发离线、实时数据融合分析的数据大屏应用。
Linux入门到精通
本套课程是从入门开始的Linux学习课程,适合初学者阅读。由浅入深案例丰富,通俗易懂。主要涉及基础的系统操作以及工作中常用的各种服务软件的应用、部署和优化。即使是零基础的学员,只要能够坚持把所有章节都学完,也一定会受益匪浅。
目录
相关文章
|
17天前
|
调度 Windows
|
1月前
|
数据可视化 数据库 C++
Qt 5.14.2揭秘高效开发:如何用VS2022快速部署Qt 5.14.2,打造无与伦比的Windows应用
Qt 5.14.2揭秘高效开发:如何用VS2022快速部署Qt 5.14.2,打造无与伦比的Windows应用
|
4月前
|
监控 安全 API
5.9 Windows驱动开发:内核InlineHook挂钩技术
在上一章`《内核LDE64引擎计算汇编长度》`中,`LyShark`教大家如何通过`LDE64`引擎实现计算反汇编指令长度,本章将在此基础之上实现内联函数挂钩,内核中的`InlineHook`函数挂钩其实与应用层一致,都是使用`劫持执行流`并跳转到我们自己的函数上来做处理,唯一的不同的是内核`Hook`只针对`内核API`函数,但由于其身处在`最底层`所以一旦被挂钩其整个应用层都将会受到影响,这就直接决定了在内核层挂钩的效果是应用层无法比拟的,对于安全从业者来说学会使用内核挂钩也是很重要。
40 1
5.9 Windows驱动开发:内核InlineHook挂钩技术
|
4月前
|
监控 API C++
8.4 Windows驱动开发:文件微过滤驱动入门
MiniFilter 微过滤驱动是相对于`SFilter`传统过滤驱动而言的,传统文件过滤驱动相对来说较为复杂,且接口不清晰并不符合快速开发的需求,为了解决复杂的开发问题,微过滤驱动就此诞生,微过滤驱动在编写时更简单,多数`IRP`操作都由过滤管理器`(FilterManager或Fltmgr)`所接管,因为有了兼容层,所以在开发中不需要考虑底层`IRP`如何派发,更无需要考虑兼容性问题,用户只需要编写对应的回调函数处理请求即可,这极大的提高了文件过滤驱动的开发效率。
41 0
|
4月前
|
监控 Windows
7.4 Windows驱动开发:内核运用LoadImage屏蔽驱动
在笔者上一篇文章`《内核监视LoadImage映像回调》`中`LyShark`简单介绍了如何通过`PsSetLoadImageNotifyRoutine`函数注册回调来`监视驱动`模块的加载,注意我这里用的是`监视`而不是`监控`之所以是监视而不是监控那是因为`PsSetLoadImageNotifyRoutine`无法实现参数控制,而如果我们想要控制特定驱动的加载则需要自己做一些事情来实现,如下`LyShark`将解密如何实现屏蔽特定驱动的加载。
32 0
7.4 Windows驱动开发:内核运用LoadImage屏蔽驱动
|
20天前
|
监控 安全 API
7.3 Windows驱动开发:内核监视LoadImage映像回调
在笔者上一篇文章`《内核注册并监控对象回调》`介绍了如何运用`ObRegisterCallbacks`注册`进程与线程`回调,并通过该回调实现了`拦截`指定进行运行的效果,本章`LyShark`将带大家继续探索一个新的回调注册函数,`PsSetLoadImageNotifyRoutine`常用于注册`LoadImage`映像监视,当有模块被系统加载时则可以第一时间获取到加载模块信息,需要注意的是该回调函数内无法进行拦截,如需要拦截则需写入返回指令这部分内容将在下一章进行讲解,本章将主要实现对模块的监视功能。
37 0
7.3 Windows驱动开发:内核监视LoadImage映像回调
|
Windows
1、从汇编语言到Windows内核编程笔记(1)
  汇编部分1、call 的本质相当于push+jmp,ret的本质相当于pop+jmp。 2、Windows中,不管哪种调用方式都是返回值放在eax中,然后返回。外部从eax中得到值。 3、Ebp总是被我们用来保存这个函数执行之前的esp的值。
1009 0
|
Web App开发 Windows
2、从汇编语言到Windows内核编程笔记(2)
内核线程 在驱动中生成的线程一般是系统线程。系统线程所在的进程名为“System”。 NTSTATUS PsCreateSystemThread( OUT PHANDLE ThreadHandle, IN ULONG DesiredAccess, IN POBJECT_ATTRIBU...
844 0
|
Web App开发 C++ Windows
3、从汇编语言到Windows内核编程笔记(3)
Windows内核(一).sys放在Drivers目录下。运行在R0层。在WDK的相应环境中,进行相应代码目录,build.一个内核程序被看作一个PE格式的DLL,它是被Windows整个内核调用的一个DLL,一旦加裁,就成为内核的组成部分。
933 0
|
Web App开发 Windows
4、从汇编语言到Windows内核编程笔记(4)
了解机器码 X86所有指令的机器码长度不定,且连续排列,因此读取机器码的唯一方法是从头开始逐条解析指令。 nop指令是单字节,可以用作填充替换长指令后的多余区域。 XDE32反汇编引擎。 关于进一步机器码的构成分析,可以看[6]。
938 0

热门文章

最新文章