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

PsSetCreateProcessNotifyRoutineEx和PsSetCreateThreadNotifyRoutine是Windows操作系统提供的两个内核回调函数,它们允许开发者在进程或线程发生创建事件时拦截并处理这些事件。这两个函数提供的回调机制是操作系统提供的最基本、最常用的内核监控进程与线程的方式。

PsSetCreateProcessNotifyRoutineEx和PsSetCreateThreadNotifyRoutine的使用方式和参数类型类似,它们都需要开发者提供一个回调函数,当进程或线程被创建时,操作系统会调用这个回调函数。这个回调函数需要满足一定的约束条件,例如不能阻塞或挂起进程或线程的创建或访问,不能调用一些内核API函数等。

PsSetCreateProcessNotifyRoutineEx和PsSetCreateThreadNotifyRoutine的主要区别在于它们所监控的事件不同。PsSetCreateProcessNotifyRoutineEx用于监控进程的创建事件,当有新的进程被创建时,操作系统会调用注册的回调函数。而PsSetCreateThreadNotifyRoutine用于监控线程的创建事件,当有新的线程被创建时,操作系统会调用注册的回调函数。

内核监控进程PsSetCreateProcessNotifyRoutineEx和线程PsSetCreateThreadNotifyRoutine回调在安全软件、系统监控和调试工具等领域有着广泛的应用。需要注意的是,在Windows 8及更高版本的操作系统中,微软推荐开发者使用ExRegisterCallback和ExUnregisterCallback函数进行回调的注册和注销。

进程回调默认会设置CreateProcess通知,而线程回调则会设置CreateThread通知,我们来看ARK工具中的枚举效果。

  • 通常情况下:

    • PsSetCreateProcessNotifyRoutineEx 用于监控进程
    • PsSetCreateThreadNotifyRoutine 用于监控线程

监控进程的启动与退出可以使用 PsSetCreateProcessNotifyRoutineEx来创建回调,当新进程创建时会优先执行回调,我们看下微软是如何定义的结构。

// 参数1: 新进程回调函数
// 参数2: 是否注销
NTSTATUS PsSetCreateProcessNotifyRoutineEx(
[in] PCREATE_PROCESS_NOTIFY_ROUTINE_EX NotifyRoutine,
[in] BOOLEAN Remove
);

如上,该函数只有两个参数,第一个参数是回调函数,第二个参数是是否注销,通常在驱动退出时可以传入TRUE对该回调进行注销,通常情况下如果驱动关闭,则必须要注销回调,而对于MyLySharkCreateProcessNotifyEx自定义回调来说,则需要指定三个必须要有的参数传递。

// 参数1: 新进程的EProcess
// 参数2: 新进程PID
// 参数3: 新进程详细信息 (仅在创建进程时有效) VOID MyLySharkCreateProcessNotifyEx(PEPROCESS Process, HANDLE ProcessId, PPS_CREATE_NOTIFY_INFO CreateInfo)

根据如上函数定义,就可以实现监控功能了,例如我们监控如果进程名是lyshark.exe则直接CreateInfo->CreationStatus = STATUS_UNSUCCESSFUL禁止该进程打开。

#include <ntifs.h>

// 两个未公开函数导出
NTKERNELAPI PCHAR PsGetProcessImageFileName(PEPROCESS Process);
NTKERNELAPI NTSTATUS PsLookupProcessByProcessId(HANDLE ProcessId, PEPROCESS *Process); // 通过PID获得进程名
PCHAR GetProcessNameByProcessId(HANDLE ProcessId)
{
NTSTATUS st = STATUS_UNSUCCESSFUL;
PEPROCESS ProcessObj = NULL;
PCHAR string = NULL;
st = PsLookupProcessByProcessId(ProcessId, &ProcessObj);
if (NT_SUCCESS(st))
{
string = PsGetProcessImageFileName(ProcessObj);
ObfDereferenceObject(ProcessObj);
}
return string;
} // 绕过签名检查
BOOLEAN BypassCheckSign(PDRIVER_OBJECT pDriverObject)
{
#ifdef _WIN64
typedef struct _KLDR_DATA_TABLE_ENTRY
{
LIST_ENTRY listEntry;
ULONG64 __Undefined1;
ULONG64 __Undefined2;
ULONG64 __Undefined3;
ULONG64 NonPagedDebugInfo;
ULONG64 DllBase;
ULONG64 EntryPoint;
ULONG SizeOfImage;
UNICODE_STRING path;
UNICODE_STRING name;
ULONG Flags;
USHORT LoadCount;
USHORT __Undefined5;
ULONG64 __Undefined6;
ULONG CheckSum;
ULONG __padding1;
ULONG TimeDateStamp;
ULONG __padding2;
} KLDR_DATA_TABLE_ENTRY, *PKLDR_DATA_TABLE_ENTRY;
#else
typedef struct _KLDR_DATA_TABLE_ENTRY
{
LIST_ENTRY listEntry;
ULONG unknown1;
ULONG unknown2;
ULONG unknown3;
ULONG unknown4;
ULONG unknown5;
ULONG unknown6;
ULONG unknown7;
UNICODE_STRING path;
UNICODE_STRING name;
ULONG Flags;
} KLDR_DATA_TABLE_ENTRY, *PKLDR_DATA_TABLE_ENTRY;
#endif PKLDR_DATA_TABLE_ENTRY pLdrData = (PKLDR_DATA_TABLE_ENTRY)pDriverObject->DriverSection;
pLdrData->Flags = pLdrData->Flags | 0x20; return TRUE;
} // 进程回调函数
VOID My_LyShark_Com_CreateProcessNotifyEx(PEPROCESS Process, HANDLE ProcessId, PPS_CREATE_NOTIFY_INFO CreateInfo)
{
char ProcName[16] = { 0 };
if (CreateInfo != NULL)
{
strcpy_s(ProcName, 16, PsGetProcessImageFileName(Process));
DbgPrint("[LyShark] 父进程ID: %ld | 父进程名: %s | 进程名: %s | 进程路径:%wZ \n", CreateInfo->ParentProcessId, GetProcessNameByProcessId(CreateInfo->ParentProcessId), PsGetProcessImageFileName(Process), CreateInfo->ImageFileName); // 判断是否为指定进程
if (0 == _stricmp(ProcName, "lyshark.exe"))
{
// 禁止打开
CreateInfo->CreationStatus = STATUS_UNSUCCESSFUL;
}
}
else
{
strcpy_s(ProcName, 16, PsGetProcessImageFileName(Process));
DbgPrint("[LyShark] 进程[ %s ] 退出了, 程序被关闭", ProcName);
}
} VOID UnDriver(PDRIVER_OBJECT driver)
{
DWORD32 ref = 0; // 注销进程回调
ref = PsSetCreateProcessNotifyRoutineEx((PCREATE_PROCESS_NOTIFY_ROUTINE_EX)My_LyShark_Com_CreateProcessNotifyEx, TRUE);
DbgPrint("[lyshark] 注销进程回调: %d \n", ref);
} NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
NTSTATUS status; // 绕过签名检查
// LINKER_FLAGS=/INTEGRITYCHECK
BypassCheckSign(Driver); DbgPrint("hello lyshark \n"); // 创建进程回调
// 参数1: 新进程的EProcess
// 参数2: 新进程PID
// 参数3: 新进程详细信息 (仅在创建进程时有效)
status = PsSetCreateProcessNotifyRoutineEx((PCREATE_PROCESS_NOTIFY_ROUTINE_EX)My_LyShark_Com_CreateProcessNotifyEx, FALSE);
if (!NT_SUCCESS(status))
{
DbgPrint("[lyshark] 创建进程回调错误");
}
Driver->DriverUnload = UnDriver;
return STATUS_SUCCESS;
}

编译并运行这个驱动程序,我们可以在ARK工具中看到这个驱动所加载的CreateProcess的回调事件。

当驱动加载后,如果你尝试打开lyshark.exe那么会提示连接的设备没有发挥作用,我们则成功拦截了这次打开,当然如果在打开进程之前扫描其特征并根据特征拒绝进程打开,那么就可以实现一个简单的防恶意程序,进程监控在防恶意程序中也是用的最多的。

说完了PsSetCreateProcessNotifyRoutineEx回调的使用方式,LyShark将继续带大家看看线程监控如何实现,监控线程创建与监控进程差不多,检测线程需要调用PsSetCreateThreadNotifyRoutine 创建回调函数,之后就可监控系统所有线程的创建,具体实现代码如下。

#include <ntifs.h>

// 两个未公开函数导出
NTKERNELAPI PCHAR PsGetProcessImageFileName(PEPROCESS Process);
NTKERNELAPI NTSTATUS PsLookupProcessByProcessId(HANDLE ProcessId, PEPROCESS *Process);
NTKERNELAPI NTSTATUS PsLookupThreadByThreadId(HANDLE ThreadId, PETHREAD *Thread); // 绕过签名检查
BOOLEAN BypassCheckSign(PDRIVER_OBJECT pDriverObject)
{
#ifdef _WIN64
typedef struct _KLDR_DATA_TABLE_ENTRY
{
LIST_ENTRY listEntry;
ULONG64 __Undefined1;
ULONG64 __Undefined2;
ULONG64 __Undefined3;
ULONG64 NonPagedDebugInfo;
ULONG64 DllBase;
ULONG64 EntryPoint;
ULONG SizeOfImage;
UNICODE_STRING path;
UNICODE_STRING name;
ULONG Flags;
USHORT LoadCount;
USHORT __Undefined5;
ULONG64 __Undefined6;
ULONG CheckSum;
ULONG __padding1;
ULONG TimeDateStamp;
ULONG __padding2;
} KLDR_DATA_TABLE_ENTRY, *PKLDR_DATA_TABLE_ENTRY;
#else
typedef struct _KLDR_DATA_TABLE_ENTRY
{
LIST_ENTRY listEntry;
ULONG unknown1;
ULONG unknown2;
ULONG unknown3;
ULONG unknown4;
ULONG unknown5;
ULONG unknown6;
ULONG unknown7;
UNICODE_STRING path;
UNICODE_STRING name;
ULONG Flags;
} KLDR_DATA_TABLE_ENTRY, *PKLDR_DATA_TABLE_ENTRY;
#endif PKLDR_DATA_TABLE_ENTRY pLdrData = (PKLDR_DATA_TABLE_ENTRY)pDriverObject->DriverSection;
pLdrData->Flags = pLdrData->Flags | 0x20; return TRUE;
} // 线程回调函数
VOID MyCreateThreadNotify(HANDLE ProcessId, HANDLE ThreadId, BOOLEAN CreateInfo)
{
PEPROCESS eprocess = NULL;
PETHREAD ethread = NULL;
UCHAR *pWin32Address = NULL; // 通过此函数拿到程序的EPROCESS结构
PsLookupProcessByProcessId(ProcessId, &eprocess);
PsLookupThreadByThreadId(ThreadId, &ethread); if (CreateInfo)
{
DbgPrint("[lyshark] 线程TID: %1d | 所属进程名: %s | 进程PID: %1d \n", ThreadId, PsGetProcessImageFileName(eprocess), PsGetProcessId(eprocess));
/*
if (0 == _stricmp(PsGetProcessImageFileName(eprocess), "lyshark.exe"))
{
DbgPrint("线程TID: %1d | 所属进程名: %s | 进程PID: %1d \n", ThreadId, PsGetProcessImageFileName(eprocess), PsGetProcessId(eprocess)); // dt _kthread
// 寻找里面的 Win32StartAddress 并写入ret
pWin32Address = *(UCHAR**)((UCHAR*)ethread + 0x1c8);
if (MmIsAddressValid(pWin32Address))
{
*pWin32Address = 0xC3;
}
}
*/
}
else
{
DbgPrint("[LyShark] %s 线程已退出...", ThreadId);
} if (eprocess)
ObDereferenceObject(eprocess);
if (ethread)
ObDereferenceObject(ethread);
} VOID UnDriver(PDRIVER_OBJECT driver)
{
NTSTATUS status; // 注销进程回调
status = PsRemoveCreateThreadNotifyRoutine(MyCreateThreadNotify);
} NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
NTSTATUS status; DbgPrint("hello lyshark \n"); // 绕过签名检查
// LINKER_FLAGS=/INTEGRITYCHECK
BypassCheckSign(Driver); // 创建线程回调
// 参数1: 新线程ProcessID
// 参数2: 新线程ThreadID
// 参数3: 线程创建/退出标志
status = PsSetCreateThreadNotifyRoutine(MyCreateThreadNotify);
if (!NT_SUCCESS(status))
{
DbgPrint("创建线程回调错误");
} Driver->DriverUnload = UnDriver;
return STATUS_SUCCESS;
}

运行后则可监控到系统总所有线程的创建与退出,效果如下所示:

7.1 Windows驱动开发:内核监控进程与线程回调的更多相关文章

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

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

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

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

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

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

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

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

  5. windows驱动开发推荐书籍

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

  6. windows 驱动开发入门——驱动中的数据结构

    最近在学习驱动编程方面的内容,在这将自己的一些心得分享出来,供大家参考,与大家共同进步,本人学习驱动主要是通过两本书--<独钓寒江 windows安全编程> 和 <windows驱动 ...

  7. Windows驱动——读书笔记《Windows驱动开发技术详解》

    =================================版权声明================================= 版权声明:原创文章 谢绝转载  请通过右侧公告中的“联系邮 ...

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

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

  9. C++第三十八篇 -- 研究一下Windows驱动开发(二)--WDM式驱动的加载

    基于Windows驱动开发技术详解这本书 一.简单的INF文件剖析 INF文件是一个文本文件,由若干个节(Section)组成.每个节的名称用一个方括号指示,紧接着方括号后面的就是节内容.每一行就是一 ...

  10. Windows 驱动开发 - 5

    上篇<Windows 驱动开发 - 4>我们已经完毕了硬件准备. 可是我们还没有详细的数据操作,比如接收读写操作. 在WDF中进行此类操作前须要进行设备的IO控制,已保持数据的完整性. 我 ...

随机推荐

  1. 🔥 DeepVideo 智能视频生产训练营火热报名中!

    阿里云视频云和阿里云开发者学堂联合打造 国内首个视频云训练营11月8日启幕 四天直播,技术大咖亲临授课干货 全面介绍视频智能生产技术和产品 帮助开发者迅速入门视频云 已超千人报名,丰富打卡玩法礼品 活 ...

  2. L2-008 最长对称子串 (回文子串 / DP / Manacher算法)

    对给定的字符串,本题要求你输出最长对称子串的长度.例如,给定Is PAT&TAP symmetric?,最长对称子串为s PAT&TAP s,于是你应该输出11. 输入格式: 输入在一 ...

  3. AtCoder Beginner Contest 189 Personal Editorial

    第一次参加 AtCoder 的比赛,感觉还挺简单. 比赛链接:https://atcoder.jp/contests/abc189 A - Slot // Author : RioTian // Ti ...

  4. 【调试】kprobes(一)基本概念

    简介 开发人员在内核或者模块的调试过程中,往往会需要要知道其中的一些函数有无被调用.何时被调用.执行是否正确以及函数的入参和返回值是什么等等. 比较简单的做法是在内核代码对应的函数中添加日志打印信息, ...

  5. python进阶(2)--列表

    文档目录:一.访问列表元素二.更新列表三.删除元素四.列表排序五.len()与range()/list()六.创建一个包含1-10平方的列表七.复制列表两种方式八.元组:不可变的列表 -------- ...

  6. docker 服务,镜像,容器命令总结

    本文为博主原创,未经允许不得转载: 目录: 1. docker 服务相关命令 2. 镜像相关总结 3. 容器相关命令总结 1. docker 服务相关: 1. 查看docker版本及相关信息: doc ...

  7. 【TouchGFX】IAR 下实现 touchgfx Caching Bitmaps 通过文件方式获取图像资源

    1.Caching Bitmaps 修改缓存方式 2.修改 blockCopy 方法(注意:忘记返回状态导致发生错误) 3.修改分散文件将位于 ExtFlashSection section 数据重定 ...

  8. DASCTF X CBCTF 2023|无畏者先行 CRYPTO—WP

    EzRSA 1.题目信息 from Crypto.Util.number import * import random from gmpy2 import * from libnum import * ...

  9. MySQL高可用搭建方案之(MMM)

    有的时候博客内容会有变动,首发博客是最新的,其他博客地址可能会未同步,认准https://blog.zysicyj.top 注意:这篇转载文章,非原创 首发博客地址 原文地址 前言 MySQL的高可用 ...

  10. [转帖]haproxy

    HAProxy是一个使用C语言编写的自由及开放源代码软件,其提供高可用性.负载均衡,以及基于TCP和HTTP的应用程序代理. HAProxy特别适用于那些负载特大的web站点,这些站点通常又需要会话保 ...