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

驱动,开发,内核,枚举,进程,线程,obcall,回调 · 浏览次数 : 431

小编点评

# Thread Callback Generation **Ob_CALLBACK** structure is used to generate thread callbacks. It holds a list of entries, which represent callbacks that need to be executed on a specific thread. **Member Variables:** * **ListEntry** list holds callbacks. * **ULONGLONG** unknown field represents the thread ID. * **HANDLE** ObHandle field represents the callback handle. * **PVOID ObTypeAddr** pointer to the object type address. * **PVOID PreCall** pointer to the pre-call callback function. * **PVOID PostCall** pointer to the post-call callback function. **Method **generate()** is called on the **Ob_CALLBACK** structure to generate thread callbacks. It iterates over the list of entries and creates a new callback for each entry. **Example Usage:** ```c // Create a callback structure Ob_CALLBACK callback; callback.listEntry = &callback.listEntry; callback.ObTypeAddr = &callback.ObTypeAddr; callback.PreCall = &callback.PreCall; callback.PostCall = &callback.PostCall; // Generate thread callbacks generate(callback); ``` **Note:** The thread ID is stored in the **ULONGLONG** field of the callback structure. This ID can be used to identify the thread that needs to execute the callback.

正文

在笔者上一篇文章《驱动开发:内核枚举Registry注册表回调》中我们通过特征码定位实现了对注册表回调的枚举,本篇文章LyShark将教大家如何枚举系统中的ProcessObCall进程回调以及ThreadObCall线程回调,之所以放在一起来讲解是因为这两中回调在枚举是都需要使用通用结构体_OB_CALLBACK以及_OBJECT_TYPE所以放在一起来讲解最好不过。

我们来看一款闭源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并循环输出即可。

// 署名权
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: me@lyshark.com
#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.com \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.com] 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即可,修改后的代码如下。

// 署名权
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: me@lyshark.com
#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()

// 移除回调
NTSTATUS RemoveObCallback(PVOID RegistrationHandle)
{
  ObUnRegisterCallbacks(RegistrationHandle);

  return STATUS_SUCCESS;
}

VOID DriverUnload(PDRIVER_OBJECT pDriverObject)
{
}

NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegPath)
{
	NTSTATUS status = STATUS_SUCCESS;

	DbgPrint("hello lyshark.com \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;
}

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

与驱动开发:内核枚举进程与线程ObCall回调相似的内容:

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

在笔者上一篇文章`《驱动开发:内核枚举Registry注册表回调》`中我们通过特征码定位实现了对注册表回调的枚举,本篇文章`LyShark`将教大家如何枚举系统中的`ProcessObCall`进程回调以及`ThreadObCall`线程回调,之所以放在一起来讲解是因为这两中回调在枚举是都需要使用通用结构体`_OB_CALLBACK`以及`_OBJECT_TYPE`所以放在一起来讲解最好不过。

驱动开发:内核注册并监控对象回调

在笔者上一篇文章`《驱动开发:内核枚举进程与线程ObCall回调》`简单介绍了如何枚举系统中已经存在的`进程与线程`回调,本章`LyShark`将通过对象回调实现对进程线程的`句柄`监控,在内核中提供了`ObRegisterCallbacks`回调,使用这个内核`回调`函数,可注册一个`对象`回调,不过目前该函数`只能`监控进程与线程句柄操作,通过监控进程或线程句柄,可实现保护指定进程线程不被终止

驱动开发:内核监控进程与线程回调

在前面的文章中`LyShark`一直在重复的实现对系统底层模块的枚举,今天我们将展开一个新的话题,内核监控,我们以`监控进程线程`创建为例,在`Win10`系统中监控进程与线程可以使用微软提供给我们的两个新函数来实现,此类函数的原理是创建一个回调事件,当有进程或线程被创建或者注销时,系统会通过回调机制将该进程相关信息优先返回给我们自己的函数待处理结束后再转向系统层。

驱动开发:内核枚举LoadImage映像回调

在笔者之前的文章`《驱动开发:内核特征码搜索函数封装》`中我们封装实现了特征码定位功能,本章将继续使用该功能,本次我们需要枚举内核`LoadImage`映像回调,在Win64环境下我们可以设置一个`LoadImage`映像加载通告回调,当有新驱动或者DLL被加载时,回调函数就会被调用从而执行我们自己的回调例程,映像回调也存储在数组里,枚举时从数组中读取值之后,需要进行位运算解密得到地址。

驱动开发:内核枚举ShadowSSDT基址

在笔者上一篇文章`《驱动开发:Win10枚举完整SSDT地址表》`实现了针对`SSDT`表的枚举功能,本章继续实现对`SSSDT`表的枚举,ShadowSSDT中文名`影子系统服务描述表`,SSSDT其主要的作用是管理系统中的图形化界面,其`Win32`子系统的内核实现是`Win32k.sys`驱动,属于GUI线程的一部分,其自身没有导出表,枚举`SSSDT`表其与`SSDT`原理基本一致。

驱动开发:内核枚举Registry注册表回调

在笔者上一篇文章`《驱动开发:内核枚举LoadImage映像回调》`中`LyShark`教大家实现了枚举系统回调中的`LoadImage`通知消息,本章将实现对`Registry`注册表通知消息的枚举,与`LoadImage`消息不同`Registry`消息不需要解密只要找到`CallbackListHead`消息回调链表头并解析为`_CM_NOTIFY_ENTRY`结构即可实现枚举。

驱动开发:内核监控Register注册表回调

在笔者前一篇文章`《驱动开发:内核枚举Registry注册表回调》`中实现了对注册表的枚举,本章将实现对注册表的监控,不同于32位系统在64位系统中,微软为我们提供了两个针对注册表的专用内核监控函数,通过这两个函数可以在不劫持内核API的前提下实现对注册表增加,删除,创建等事件的有效监控,注册表监视通常会通过`CmRegisterCallback`创建监控事件并传入自己的回调函数,与该创建对应的是

驱动开发:内核测试模式过DSE签名

微软在`x64`系统中推出了`DSE`保护机制,DSE全称`(Driver Signature Enforcement)`,该保护机制的核心就是任何驱动程序或者是第三方驱动如果想要在正常模式下被加载则必须要经过微软的认证,当驱动程序被加载到内存时会验证签名的正确性,如果签名不正常则系统会拒绝运行驱动,这种机制也被称为驱动强制签名,该机制的作用是保护系统免受恶意软件的破坏,是提高系统安全性的一种手段

驱动开发:内核无痕隐藏自身分析

在笔者前面有一篇文章`《驱动开发:断链隐藏驱动程序自身》`通过摘除驱动的链表实现了断链隐藏自身的目的,但此方法恢复时会触发PG会蓝屏,偶然间在网上找到了一个作者介绍的一种方法,觉得有必要详细分析一下他是如何实现的驱动隐藏的,总体来说作者的思路是最终寻找到`MiProcessLoaderEntry`的入口地址,该函数的作用是将驱动信息加入链表和移除链表,运用这个函数即可动态处理驱动的添加和移除问题。

驱动开发:内核监视LoadImage映像回调

在笔者上一篇文章`《驱动开发:内核注册并监控对象回调》`介绍了如何运用`ObRegisterCallbacks`注册`进程与线程`回调,并通过该回调实现了`拦截`指定进行运行的效果,本章`LyShark`将带大家继续探索一个新的回调注册函数,`PsSetLoadImageNotifyRoutine`常用于注册`LoadImage`映像监视,当有模块被系统加载时则可以第一时间获取到加载模块信息,需要