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

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

6.9 Windows驱动开发:内核枚举进线程ObCall回调的更多相关文章

  1. X64驱动:内核操作进线程/模块

    注意:下面的所有案例必须使用.C结尾的文件,且必须在链接选项中加入 /INTEGRITYCHECK 选项,否则编译根本无法通过(整合修正,Win10可编译,须在测试模式下进行),内核代码相对固定,如果 ...

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

    在笔者上一篇文章<驱动开发:内核枚举Registry注册表回调>中我们通过特征码定位实现了对注册表回调的枚举,本篇文章LyShark将教大家如何枚举系统中的ProcessObCall进程回 ...

  3. Windows驱动开发-内核常用内存函数

    搞内存常用函数 C语言 内核 malloc ExAllocatePool memset RtlFillMemory memcpy RtlMoveMemory free ExFreePool

  4. 《Windows内核安全与驱动开发》 4.4 线程与事件

    <Windows内核安全与驱动开发>阅读笔记 -- 索引目录 <Windows内核安全与驱动开发> 4.4 线程与事件 一.开辟一个线程,参数为(打印内容+打印次数),利用线程 ...

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

    在笔者上一篇文章<驱动开发:内核枚举LoadImage映像回调>中LyShark教大家实现了枚举系统回调中的LoadImage通知消息,本章将实现对Registry注册表通知消息的枚举,与 ...

  6. windows驱动开发推荐书籍

    [作者] 猪头三 个人网站 :http://www.x86asm.com/ [序言] 很多人都对驱动开发有兴趣,但往往找不到正确的学习方式.当然这跟驱动开发的本土化资料少有关系.大多学的驱动开发资料都 ...

  7. Windows驱动开发-IRP的完成例程

    <Windows驱动开发技术详解 >331页, 在将IRP发送给底层驱动或其他驱动之前,可以对IRP设置一个完成例程,一旦底层驱动将IRP完成后,IRP完成例程立刻被处罚,通过设置完成例程 ...

  8. C++第三十三篇 -- 研究一下Windows驱动开发(一)内部构造介绍

    因为工作原因,需要做一些与网卡有关的测试,其中涉及到了驱动这一块的知识,虽然程序可以运行,但是不搞清楚,心里总是不安,觉得没理解清楚.因此想看一下驱动开发.查了很多资料,看到有人推荐Windows驱动 ...

  9. Windows驱动开发(中间层)

    Windows驱动开发 一.前言 依据<Windows内核安全与驱动开发>及MSDN等网络质料进行学习开发. 二.初步环境 1.下载安装WDK7.1.0(WinDDK\7600.16385 ...

  10. [Windows驱动开发](一)序言

    笔者学习驱动编程是从两本书入门的.它们分别是<寒江独钓——内核安全编程>和<Windows驱动开发技术详解>.两本书分别从不同的角度介绍了驱动程序的制作方法. 在我理解,驱动程 ...

随机推荐

  1. 玩转Python:用Python处理文档,5个必备的库,特别实用,附代码

    在Python中,有几个流行的库用于处理文档,包括解析.生成和操作文档内容.以下是一些常用的库及其简介和简单的代码示例: PyPDF2 - 用于处理PDF文件. 简介:PyPDF2是一个纯Python ...

  2. Grafana--Min step与Resolution

    问题: 今天在统计机房请求量的时候,发现时间选择12 hours时还是正常的,但是选择24 hours时就有一些线条出不来,数据也有缺失,如下: 12 hours 24 hours 问了同事,说是数据 ...

  3. 0.o?让我看看怎么个事儿之SpringBoot自动配置

    学习 SpringBoot 自动配置之前我们需要一些前置知识点: Java注解,看完就会用 学会@ConfigurationProperties月薪过三千 不是银趴~是@Import! @Condit ...

  4. 【网络爬虫学习】第一个Python爬虫程序 & 编码与解码详解 & Pythonの实现

    本节编写一个最简单的爬虫程序,作为学习 Python 爬虫前的开胃小菜. 下面使用 Python 内置的 urllib 库获取网页的 html 信息.注意,urllib 库属于 Python 的标准库 ...

  5. signed main 和 int main 的区别

    事实上只是因为有人直接 #define int long long 了...然后int main改成signed main就行了 #define int long long ... signed ma ...

  6. Codeforces Round #706 Editorial

    1496A. Split it! 类回文判断,只要 k = 0 或者 \(s[1,k] 和 s[n - k + 1,n]\)是回文即可 特判情况 n < 2 * k + 1 为 NO int m ...

  7. windows 系统关闭占用端口的应用

    开发中有时候开发工具把程序关闭了,但是后台并没有真正关闭程序,导致再次启动相同端口的程序时报端口已经被使用的错误,这时如何强制关闭已占用的端口 1.打开dos对话框 2.查找被占用的端口的进程号 ne ...

  8. util工具函数

    1 /** 2 * @param {Function} fn 防抖函数 3 * @param {Number} delay 延迟时间 4 */ 5 export function debounce(f ...

  9. C#将汉字转换为拼音

    首先上效果图 方法调用 private void txt_Chinese_TextChanged(object sender, EventArgs e) { txt_PinYIn.Text = //调 ...

  10. http-长连接

    1. 短链接 http1.0 -- 1个请求-响应过程会创建且1个新的连接 2. 长连接 http1.1 -- 同域下可以创建1个tcp连接,多个请求在同一个tcp上串行处理请求 http2.0 -- ...