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

2023-12-07 19:01

本文主要是介绍6.9 Windows驱动开发:内核枚举进线程ObCall回调,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在笔者上一篇文章《内核枚举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;
}

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

这篇关于6.9 Windows驱动开发:内核枚举进线程ObCall回调的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/467004

相关文章

快速修复一个Panic的Linux内核的技巧

《快速修复一个Panic的Linux内核的技巧》Linux系统中运行了不当的mkinitcpio操作导致内核文件不能正常工作,重启的时候,内核启动中止于Panic状态,该怎么解决这个问题呢?下面我们就... 感谢China编程(www.chinasem.cn)网友 鸢一雨音 的投稿写这篇文章是有原因的。为了配置完

Go语言开发实现查询IP信息的MCP服务器

《Go语言开发实现查询IP信息的MCP服务器》随着MCP的快速普及和广泛应用,MCP服务器也层出不穷,本文将详细介绍如何在Go语言中使用go-mcp库来开发一个查询IP信息的MCP... 目录前言mcp-ip-geo 服务器目录结构说明查询 IP 信息功能实现工具实现工具管理查询单个 IP 信息工具的实现服

Windows 上如果忘记了 MySQL 密码 重置密码的两种方法

《Windows上如果忘记了MySQL密码重置密码的两种方法》:本文主要介绍Windows上如果忘记了MySQL密码重置密码的两种方法,本文通过两种方法结合实例代码给大家介绍的非常详细,感... 目录方法 1:以跳过权限验证模式启动 mysql 并重置密码方法 2:使用 my.ini 文件的临时配置在 Wi

JAVA保证HashMap线程安全的几种方式

《JAVA保证HashMap线程安全的几种方式》HashMap是线程不安全的,这意味着如果多个线程并发地访问和修改同一个HashMap实例,可能会导致数据不一致和其他线程安全问题,本文主要介绍了JAV... 目录1. 使用 Collections.synchronizedMap2. 使用 Concurren

C 语言中enum枚举的定义和使用小结

《C语言中enum枚举的定义和使用小结》在C语言里,enum(枚举)是一种用户自定义的数据类型,它能够让你创建一组具名的整数常量,下面我会从定义、使用、特性等方面详细介绍enum,感兴趣的朋友一起看... 目录1、引言2、基本定义3、定义枚举变量4、自定义枚举常量的值5、枚举与switch语句结合使用6、枚

usb接口驱动异常问题常用解决方案

《usb接口驱动异常问题常用解决方案》当遇到USB接口驱动异常时,可以通过多种方法来解决,其中主要就包括重装USB控制器、禁用USB选择性暂停设置、更新或安装新的主板驱动等... usb接口驱动异常怎么办,USB接口驱动异常是常见问题,通常由驱动损坏、系统更新冲突、硬件故障或电源管理设置导致。以下是常用解决

Windows Docker端口占用错误及解决方案总结

《WindowsDocker端口占用错误及解决方案总结》在Windows环境下使用Docker容器时,端口占用错误是开发和运维中常见且棘手的问题,本文将深入剖析该问题的成因,介绍如何通过查看端口分配... 目录引言Windows docker 端口占用错误及解决方案汇总端口冲突形成原因解析诊断当前端口情况解

Linux内核参数配置与验证详细指南

《Linux内核参数配置与验证详细指南》在Linux系统运维和性能优化中,内核参数(sysctl)的配置至关重要,本文主要来聊聊如何配置与验证这些Linux内核参数,希望对大家有一定的帮助... 目录1. 引言2. 内核参数的作用3. 如何设置内核参数3.1 临时设置(重启失效)3.2 永久设置(重启仍生效

基于Java实现回调监听工具类

《基于Java实现回调监听工具类》这篇文章主要为大家详细介绍了如何基于Java实现一个回调监听工具类,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录监听接口类 Listenable实际用法打印结果首先,会用到 函数式接口 Consumer, 通过这个可以解耦回调方法,下面先写一个

使用Python开发一个带EPUB转换功能的Markdown编辑器

《使用Python开发一个带EPUB转换功能的Markdown编辑器》Markdown因其简单易用和强大的格式支持,成为了写作者、开发者及内容创作者的首选格式,本文将通过Python开发一个Markd... 目录应用概览代码结构与核心组件1. 初始化与布局 (__init__)2. 工具栏 (setup_t